本文将介绍gRPC在golang中的基础使用方法,包含基础了gRPC使用场景,包括四种RPC方式、metadata的使用等。
本文文章所用示例代码见go-grpc-example。
参考文档:PROTOCOL-HTTP2、Core-Concept、grpc-go文档、Doc
导包
go get google.golang.org/grpc
四种RPC方式
Unary RPC
客户端发送一次请求,然后服务端返回一次响应,这个就是Unary RPC。
例如下面的例子,客户端每发送一个 CreateCellphoneRequest就会得到服务端的 CreateCellphoneResponse响应。
rpc CreateCellphone(CreateCellphoneRequest) returns (CreateCellphoneResponse);
Server streaming RPC
客户端发送一次请求,服务器不再以一次性的方式返回响应,而是以数据流的方式返回响应(a stream of messages),这个就是Server streaming RPC。
在proto文件内定义rpc服务的时候,在返回值类型前面加上 stream关键字,就完成了这种类型的定义。
在这种模式下,服务端涉及的方法为 Send,用来发送数据流;客户端涉及的方法为 Recv,用来接收数据流。
例如下面的例子,客户端接收流式响应,每次接收都得到一个 Cellphone类型的数据
rpc SearchCellphone(FilterCondition) returns (stream Cellphone);
Client streaming RPC
客户端发送请求的时候以数据流的方式发送请求数据(a stream of messages),服务端接收数据流,然后返回一次性的响应数据。
这种模式下,客户端涉及的方法为 Send用来发送流数据,CloseAndRecv用来接收响应数据;服务端涉及的方法为 Recv接收请求数据流,SendAndClose用来发送响应数据。
例如下面的例子,客户端发送字节流数据给服务端,服务端处理完成后,返回一个响应。
rpc UploadCellphoneCover(stream UploadCellphoneCoverRequest) returns (UploadCellphoneCoverResponse);
注意事项:
- 在golang中的gRPC实现中,客户端调用
Send方法发送数据的时候,并不会阻塞等待服务端调用Recv?(从测试来看好像这样)。服务端有可能随时通过返回错误来关闭流,所以客户端每次Send或者最后CloseAndRecv之后都要判断错误。 - 如果客户端在
Send过程中发生了在客户端这一侧的错误,那么直接返回对应的error;但是如果是服务端返回了错误,那么在客户端再使用Send的时候,就会笼统地返回io.EOF错误,更为具体的错误需要在客户端侧调用RecvMsg(nil)来获得。
Bidirectional streaming RPC
客户端和服务端之间双向的数据交互都是数据流,这种方式可以发送多个请求和多个响应。
例如下面的例子。
rpc BuyCellphone(stream BuyCellphoneRequest) returns (stream BuyCellphoneResponse);
注意事项:
- 当客户端发送完所有的请求数据之后,调用
CloseSend方法关闭写端。 - 服务端通过
Recv不断接收请求,使用Send发送响应。
gRPC的响应状态使用
gRPC中表示响应的状态和状态码要用到这两个package
google.golang.org/grpc/codes
google.golang.org/grpc/status
通过 status.Error(c codes.Code, msg string) error这个接口可以返回响应错误信息
gRPC定义了一系列内置的错误码,可以在codes中设置,常见的错误码比如 codes.OK, codes.Canceled, codes.InvalidArgument等。
context.Context在gRPC中的使用
超时控制
使用 context.WithTimeout或者 context.WithDeadline
调用取消
使用 context.WithCancel
gPRC中时间类型的使用
在proto文件中 import "google/protobuf/timestamp.proto",这个是protobuf的内置message类型,用来表示时间戳。
生成的go代码中,类型为 timestamppb.Timestamp。
与 time.Time的相互转换:
timestamppb.Timestamp -> time.Time: AsTime方法可以转化为go的内置 time.Time类型,timestamppb.Timestamp.AsTime()
time.Time -> timestamppb.Timestamp:timestamppb.New(time.Time)。
gRPC中的metadata/trailer
metadata
gRPC中的metadata是可以在传输携带的一组键值对数据,键值对的类型一般都是字符串,也可以是二进制数据。
获取metadata
使用grpc中的metadata包来获取(google.golang.org/grpc/metadata),metadata依附在context.Context中。
创建带有metadata的Context
使用 metadata.NewOutgoingContext函数完成context的创建;或者使用 metadata.AppendToOutgoingContext函数。
response trailer/header
服务端响应的时候,除了主体信息外,还可以额外携带header信息和trailer信息,即gRPC完成响应后额外传输的一组数据,也是键值对的形式。
Unary RPC和Streaming RPC获取响应的trailer和header不一样。
- Unary RPC获取响应的trailer/header:使用
grpc.Trailer(&metadata.MD)和grpc.Header(&metadata.MD)生成grpc.CallOption,作为选项传入。 - Streaming RPC获取trailer/header:在stream上调用
Trailer()或者Header()方法。