GRPC文档阅读心得

主要是两个文档, 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 issues1grpc-go issues2 以及 HTTP2文档 才发现, 由于HTTP2有多路复用的特性, 对于同一个sever, 只需要维护一个连接就好了, 没有必要用多个连接去并行复用数据流. 连接数量减少对提升 HTTPS 部署的性能来说是一项特别重要的功能:可以减少开销较大的 TLS 连接数、提升会话重用率,以及从整体上减少所需的客户端和服务器资源。