Bidirectional Stream
同理,当客户端、服务端同时都在发送消息也是支持的。
// 服务端 func (o *Order) BdStream(rs v1.OrderService_BdStreamServer) error { var value []int64 for { recv, err := rs.Recv() if err == io.EOF { log.Println(value) return nil } if err != nil { panic(err) } value = append(value, recv.OrderId) log.Printf("BdStream receiv msg %v", recv.OrderId) rs.SendMsg(&v1.Order{ OrderId: recv.OrderId, Reason: nil, }) } return nil } // 客户端 for i := 0; i < 5; i++ { messages, _ := GetMsg(data) // 发送消息 rpc.SendMsg(messages[0]) // 接收消息 receive, _ := rpc.RecvMsg() marshalIndent, _ := json.MarshalIndent(receive, "", "\t") fmt.Println(string(marshalIndent)) } rpc.CloseSend()
其实就是将上诉两则合二为一。
通过调用示例很容易理解。
元数据
gRPC
也支持元数据传输,类似于 HTTP
中的 header
。
// 客户端写入 metaStr := `{"lang":"zh"}` var m map[string]string err := json.Unmarshal([]byte(metaStr), &m) md := metadata.New(m) // 调用时将 ctx 传入即可 ctx := metadata.NewOutgoingContext(context.Background(), md) // 服务端接收 md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, status.Errorf(codes.DataLoss, "failed to get metadata") } fmt.Println(md)
gRPC gateway
gRPC
虽然功能强大使用也很简单,但对于浏览器、APP的支持还是不如 REST 应用广泛(浏览器也支持,但应用非常少)。
为此社区便创建了 github.com/grpc-ecosys… 项目,可以将 gRPC 服务暴露为 RESTFUL API。
为了让测试可以习惯用 postman 进行接口测试,我们也将 gRPC 服务代理出去,更方便的进行测试。
反射调用
作为一个 rpc 框架,泛化调用也是必须支持的,可以方便开发配套工具;gRPC 是通过反射支持的,通过拿到服务名称、pb 文件进行反射调用。
github.com/jhump/proto… 这个库封装了常见的反射操作。
上图中看到的可视化 stream
调用也是通过这个库实现的。
负载均衡
由于 gRPC
是基于 HTTP/2
实现的,客户端和服务端会保持长连接;这时做负载均衡就不像是 HTTP
那样简单了。
而我们使用 gRPC
想达到效果和 HTTP 是一样的,需要对请求进行负载均衡而不是连接。
通常有两种做法:
- 客户端负载均衡
- 服务端负载均衡
客户端负载均衡在 rpc
调用中应用广泛,比如 Dubbo
就是使用的客户端负载均衡。
gRPC
中也提供有相关接口,具体可以参考官方demo。
客户端负载均衡相对来说对开发者更灵活(可以自定义适合自己的策略),但相对的也需要自己维护这块逻辑,如果有多种语言那就得维护多份。
所以在云原生这个大基调下,更推荐使用服务端负载均衡。
可选方案有:
- istio
- envoy
- apix
这块我们也在研究,大概率会使用 envoy/istio
。
总结
gRPC
内容还是非常多的,本文只是作为一份入门资料希望能让不了解 gRPC
的能有一个基本认识;这在云原生时代确实是一门必备技能。
对文中的 gRPC 客户端感兴趣的朋友,可以参考这里的源码: