五分钟学会gRPC,你学会了吗?

发布时间:2025-04-06 点击:17
介绍
我猜测大部分长期使用 java 的开发者应该较少会接触 grpc,毕竟在 java 圈子里大部分使用的还是 dubbo/springclound 这两类服务框架。
我也是近段时间有机会从零开始重构业务才接触到 grpc 的,当时选择grpc 时也有几个原因:
基于云原生的思路开发部署项目,而在云原生中 grpc 几乎已经是标准的通讯协议了。 开发语言选择了 go,在 go 圈子中 grpc 显然是更好的选择。 公司内部有部分业务使用的是 python 开发,在多语言兼容性上 grpc 支持的非常好。
经过线上一年多的平稳运行,可以看出 grpc 还是非常稳定高效的;rpc 框架中最核心的几个要点:序列化 通信协议 idl(接口描述语言)
这些在 grpc 中分别对应的是: 基于 protocol buffer 序列化协议,性能高效。 基于 http/2 标准协议开发,自带 stream、多路复用等特性;同时由于是标准协议,第三方工具的兼容性会更好(比如负载均衡、监控等)。编写一份 .proto 接口文件,便可生成常用语言代码。 http/2
学习 grpc 之前首先得知道它是通过什么协议通信的,我们日常不管是开发还是应用基本上接触到最多的还是 http/1.1 协议。
由于 http/1.1 是一个文本协议,对人类非常友好,相反的对机器性能就比较低。
需要反复对文本进行解析,效率自然就低了;要对机器更友好就得采用二进制,http/2 自然做到了。
除此之外还有其他优点: 多路复用:可以并行的收发消息,互不影响。hpack 节省 header 空间,避免 http1.1 对相同的 header 反复发送。 protocol
grpc 采用的是 protocol 序列化,发布时间比 grpc 早一些,所以也不仅只用于 grpc,任何需要序列化 io 操作的场景都可以使用它。
它会更加的省空间、高性能;之前在开发 https://github.com/crossoverjie/cim 时就使用它来做数据交互。package order.v1; service orderservice{ rpc create(orderapicreate) returns (order) {} rpc close(closeapicreate) returns (order) {} // 服务端推送 rpc serverstream(orderapicreate) returns (stream order) {} // 客户端推送 rpc clientstream(stream orderapicreate) returns (order) {} // 双向推送 rpc bdstream(stream orderapicreate) returns (stream order) {} } message orderapicreate{ int64 order_id = 1; repeated int64 user_id = 2; string remark = 3; repeated int32 reason_id = 4; }使用起来也是非常简单的,只需要定义自己的 .proto 文件,便可用命令行工具生成对应语言的 sdk。
具体可以参考官方文档:https://grpc.io/docs/languages/go/generated-code/ 调用 protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ test.proto
生成代码之后编写服务端就非常简单了,只需要实现生成的接口即可。func (o *order) create(ctx context.context, in *v1.orderapicreate) (*v1.order, error) { // 获取 metadata md, ok := metadata.fromincomingcontext(ctx) if !ok { return nil, status.errorf(codes.dataloss, "failed to get metadata") } fmt.println(md) fmt.println(in.orderid) return &v1.order{ orderid: in.orderid, reason: nil, }, nil }
客户端也非常简单,只需要依赖服务端代码,创建一个 connection 然后就和调用本地方法一样了。
这是经典的 unary(一元)调用,类似于 http 的请求响应模式,一个请求对应一次响应。
server stream
grpc 除了常规的 unary 调用之外还支持服务端推送,在一些特定场景下还是很有用的。
func (o *order) serverstream(in *v1.orderapicreate, rs v1.orderservice_serverstreamserver) error { for i := 0; i < 5; i++ { rs.send(&v1.order{ orderid: in.orderid, reason: nil, }) } return nil }
服务端的推送如上所示,调用 send 函数便可向客户端推送。 for { msg, err := rpc.recvmsg() if err == io.eof { marshalindent, _ := json.marshalindent(msgs, "", "\t") fmt.println(msg) return } }客户端则通过一个循环判断当前接收到的数据包是否已经截止来获取服务端消息。
为了能更直观的展示这个过程,优化了之前开发的一个 grpc 客户端,可以直观的调试 stream 调用。
上图便是一个服务端推送示例。client stream
除了支持服务端推送之外,客户端也支持。
客户端在同一个连接中一直向服务端发送数据,服务端可以并行处理消息。// 服务端代码func (o *order) clientstream(rs v1.orderservice_clientstreamserver) error { var value []int64 for { recv, err := rs.recv() if err == io.eof { rs.sendandclose(&v1.order{ orderid: 100, reason: nil, }) log.println(value) return nil } value = append(value, recv.orderid) log.printf("clientstream receiv msg %v", recv.orderid) } log.println("clientstream finish") return nil } // 客户端代码 for i := 0; i < 5; i++ { messages, _ := getmsg(data) rpc.sendmsg(messages[0]) } receive, err := rpc.closeandreceive()代码与服务端推送类似,只是角色互换了。
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)

网站客户快乐的秘密
微商模式设计与落地 移动互联网企业下一个
教你测试美国主机的速度快慢
SEO优化人员必须要掌握的SEO优化技巧
网站开发bc网站建设bC网站制作开发全天24小时热线;安卓网站客户端开发
怎么建网站,个人或企业网页制作,四个要点不可少
论SEO优化对网站建设的重要性
安徽网站推广_蚌埠公司网站关键词优化要注意哪些方法?