1. Status of server-side response
Developers doing back-end services are always sensitive to error handling, so they will always be very careful when doing the service response (response/reply) design.
If the back-end service is selected from HTTP API (rest api), such as json over http, the API response (Response) will mostly contain the following information.
In the response design of this http api, the first two states identify the response status of the request. This status consists of a status code (code) and a status message (msg). The status message is a detailed explanation of the cause of the error corresponding to the status code. The payload follows only when the status is normal (code = 0), and the payload is obviously the business information intended to be passed to the client in the response.
Such a service response design is currently more common and mature program, it is also very easy to understand.
Okay, now let’s look at another big class of services: services provided by RPC. Let’s take the most widely used gRPC as an example. In gRPC, a service is defined as follows (let’s borrow the helloworld example provided by grpc-go).
grpc has a constraint for each rpc method (e.g. SayHello) that it can only have one input parameter and one return value. The go code generated by this .proto definition via protoc becomes this.
We see that for the SayHello RPC method, the go code generated by protoc has an additional error return value in the SayHello method’s return value list that Gopher’s are familiar with. For gophers who are used to the HTTP API response design, here’s the problem! Is the code and msg in the http api response that represent the status of the response defined in the HelloReply business response data, or is it returned via error? This grpc official documentation does not seem to specify (if you find the location, you can tell me oh).
2. gRPC server-side response design ideas
We are not in a hurry to draw conclusions! We continue to borrow helloworld this sample program to test the response of the client when the error return value is not nil! First, change the code of greeter_server.
In the above code, we deliberately construct an error and return it to the client that called the method. Let’s run the service and start greeter_client to access the service. On the client side, we get the following result are as follows.
From the output of the client, we see the content of our custom error (test grpc error). But we also find that the error output also has a “code = Unknown” output. It seems that grpc expects the error to be in the form of code and desc.
At this point, I have to check the gprc-go (v1.40.0) reference documentation! In the documentation of grpc-go we find several functions related to Error that are DEPRECATED.
The documentation for these deprecated functions mentions replacing them with functions of the same name from the status package. So who is this status package? Looking through the source code of grpc-go, we finally found the status package, and we found the answer in the first sentence of the package description.
It turns out that the status package implements the type of error expected by the grpc client above. So what does this type look like? Let’s trace the code step by step.
In the grpc-go/status package we see the following code.
The status package uses Status from the internal/status package. Let’s look again at the definition of the Status structure in the internal/status package.
The Status structure of the internal/status package combines a field of type *spb.Status (the type in the google.golang.org/genproto/googleapis/rpc/statu s package). Continue tracing the spb.Status.
We see that this last Status structure contains Code and Message, so it is clear that grpc is designed to expect the developer to include the response status of the rpc method in the return value of error, while the custom response structure only needs to contain the data required by the business. Let’s use a diagram to establish the mapping between the http api and the rpc response horizontally.
With this diagram in hand, we’ll be well-equipped to face the problem of how to design grpc method responses!
grpc-go defines more than 10 error codes required by the grpc specification in the codes package.
In addition to these standard error codes, we can also extend to define our own error codes and error descriptions.
3. How the server side constructs error and how the client side parses error
As mentioned earlier, the gRPC server uses the last return value of the rpc method, error, to carry the response status. The
google.golang.org/grpc/status package provides some convenient functions for constructing client-side parsable error, let’s look at the following example (based on the above helloworld’s greeter_server) blob/master/examples/helloworld/greeter_server/main.go) is modified).
The status package provides a function similar to fmt.Errorf, so we can easily construct an error instance with code and msg and return it to the client.
The client can also parse out the information carried in the error through the functions provided by the status package, as shown in the following code.
We see that: the information carried in the error returned by the rpc method that is not nil can be extracted very briefly by the status.Convert function.
4. Empty response
The gRPC proto file specification requires that the definition of each rpc method must contain a return value, and the return value cannot be null, such as the SayHello method in the .proto file of the above helloworld project.
If you remove the HelloReply return value, then protoc will report an error when generating code!
But some methods do not need to return business data themselves, so we need to define an empty response message for them, e.g.
Considering that every project has to repeat the wheel defined by Empty message above when it encounters an empty response, grpc officially provides an empty message that can be reused.
We just need to import that empty.proto in the .proto file and use Empty, like the following code.
google.protobuf.Empty is not just for empty responses, but also for empty requests, which is left to you to complete on your own.
In this article, we talked about gRPC server-side response design, the main point is to directly use the gRPC-generated rpc aspect of the error return value to indicate the response status of the rpc call, and not to repeat the code and msg fields in the custom Message structure to indicate the response status.
btw, to do the API error design, google this API design reference is very good. You must have time to read it well.