主要是两个文档, grpc repo的文档 https://github.com/grpc/grpc/tree/master/doc , grpc-go repo的文档 https://github.com/grpc/grpc-go/tree/master/Documentation.
grpc-go 文档
gRPC Server Reflection Tutorial
在代码中import "google.golang.org/grpc/reflection"
包, 然后加一行代码reflection.Register(s)
, 就可以启用 server reflection. 就可以用grpc_cli去进行获得服务列表, 方法列表, message结构体定义了. reflection.Register(s)实际上是注册了一个特殊的service, 它能列出server中已注册的服务和方法等信息.
Compression
用 encoding.RegisterCompressor
方法取注册一个压缩器, 启用了压缩的话, 服务端和客户端双方都要进行同样的处理, 服务端在newServer时要带上compressor的serverOption, 客户端在dail的时候要带上WithDefaultCallOptions的DialOption, DialOption加上压缩解压的处理, 不然会得到一个 Internal error
, 和HTTP方式一样, 压缩类型体现在content-type的header上.
Concurrency
Dial得到的ClientConn是并发安全.
stream的读写不是并发安全的, sendMsg或RecvMsg不能在多个goroutine中并发地调用,但可以分别在两个goroutine中处理send和Recv.
Encoding
序列化反序列化
自定义消息编码解码, 注册一个实现 Codec
接口的对象即可, 然后在Dial或Call时带上grpc.CallContentSubtype
这个CallOption, 这样就可以自动处理这个带这个content-type的请求. 默认为 proto
压缩解压缩
自定义压缩解压缩, 注册一个实现 Compressor
接口的对象即可, 然后在Dial或Call时带上grpc.UseCompressor
这个CallOption.
[Mocking Service for gRPC
](https://github.com/grpc/grpc-go/blob/master/Documentation/gomock-example.md)
主要讲如何在单元测试中mock, 用gomock命令行生成实现xx接口的代码, 没什么特别的
[Authentication
](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-auth-support.md)
主要讲如何进行身份验证, 没什么特别的
Metadata
metadata类似HTTP1中的header, 数据结构都是一样的type MD map[string][]string
,
key都是大小写不敏感的, 但实现规范和HTTP1不一样, HTTP1是按单词之间用连字符”-“分隔, 每个单词第一个字母大写这样的规范来的, 处理起来消耗更大, 而metadata是全转为小写, 实际使用过程中, 提前规范化key能提高不必要的strings.ToLower
调用.
用-bin结尾的来传递二进制数据.
服务端handler用metadata.FromIncomingContext(ctx)
拿到metadata, 客户端用metadata.AppendToOutgoingContext
来附加kv到ctx中.
如果服务端handler又想附加一些信息返回client, 那么就要通过header和trailer传递, 类似responseHeader.func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
// create and send header
header := metadata.Pairs("header-key", "val")
grpc.SendHeader(ctx, header)
// create and set trailer
trailer := metadata.Pairs("trailer-key", "val")
grpc.SetTrailer(ctx, trailer)
}
然后客户端在调用的时候传要保存的header和trailler的指针到CallOption中, 调用完后指针指向的metadata map就有数据了, 坦率地讲, 我觉得这样处理很麻烦.var header, trailer metadata.MD // variable to store header and trailer
r, err := client.SomeRPC(
ctx,
someRequest,
grpc.Header(&header), // will retrieve header
grpc.Trailer(&trailer), // will retrieve trailer
)
// do something with header and trailer
Keepalive
gRPC会定时发http2 ping帧来判断连接是否挂掉, 如果ping没有在一定时期内ack, 那么连接会被close.
Log Levels
grpc-go包默认用gpclog包打日志, grpclog包默认是用多个*log.Logger来实现日志级别, 默认输出到stderr, 对于生产环境, 肯定要集成到自己的日志流里去, 接口是个好东西, grpclog包允许setLog, 实现grpclog.LoggerV2接口即可.
info日志包括:
grpclog里的info是为了debug
- DNS 收到了更新
- 负载均衡器 更新了选择的目标
- 重要的grpc 状态变更
warn日志包括:
warning日志是出现了一些错误, 但还不至于panic. - DNS无法解析给定的target
- 连接server时出错
- 连接丢失或中断
error日志包括:
grpc内部有些error不是用户发起的函数调用, 所以无法返回error给调用者, 只能内部自己打error日志 - 函数签名没有error, 但调用方传了个错误的参数过来.
- 内部错误.
Fatal日志:
fatal日志是出现了不可恢复的内部错误, 要panic.
grpc 文档
之前一直有个误区, 多个连接比单个连接要快, 看了 grpc-go issues1、grpc-go issues2 以及 HTTP2文档 才发现, 由于HTTP2有多路复用的特性, 对于同一个sever, 只需要维护一个连接就好了, 没有必要用多个连接去并行复用数据流. 连接数量减少对提升 HTTPS 部署的性能来说是一项特别重要的功能:可以减少开销较大的 TLS 连接数、提升会话重用率,以及从整体上减少所需的客户端和服务器资源。