为了账号安全,请及时绑定邮箱和手机立即绑定

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

标签:
Photoshop

本章节将介绍 gRPC 的流式,分为三种类型:

Server-side streaming RPC:服务器端流式 RPCClient-side streaming RPC:客户端流式 RPCBidirectional streaming RPC:双向流式 RPC

任何技术,因为有痛点,所以才有了存在的必要性。如果您想要了解 gRPC 的流式调用,请继续

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

gRPC Streaming 是基于 HTTP/2 的,后续章节再进行详细讲解

流式为什么要存在呢,是 Simple RPC 有什么问题吗?通过模拟业务场景,可得知在使用 Simple RPC 时,有如下问题:

数据包过大造成的瞬时压力接收数据包时,需要所有数据包都接受成功且正确后,才能够回调响应,进行业务处理(无法客户端边发送,服务端边处理)大规模数据包实时场景

每天早上 6 点,都有一批百万级别的数据集要同从 A 同步到 B,在同步的时候,会做一系列操作(归档、数据分析、画像、日志等)。这一次性涉及的数据量确实大

在同步完成后,也有人马上会去查阅数据,为了新的一天筹备。也符合实时性。

两者相较下,这个场景下更适合使用 Streaming RPC

在讲解具体的 gRPC 流式代码时,会着重在第一节讲解,因为三种模式其实是不同的组合。希望你能够注重理解,举一反三,其实都是一样的知识点

目录结构

$ tree go-grpc-example

go-grpc-example

├── client

│ ├── simple_client

│ │ └── client.go

│ └── stream_client

│ └── client.go

├── proto

│ ├── searchto

│ └── streamto

└── server

├── simple_server

│ └── server.go

└── stream_server

└── server.go

增加 stream_server、stream_client 存放古玩服务端和客户端文件,proto/streamto 用于编写 IDL

在 proto 文件夹下的 streamto 文件中,写入如下内容:

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

注意关键字 stream,声明其为一个流方法。这里共涉及三个方法,对应关系为

List:服务器端流式 RPCRecord:客户端流式 RPCRoute:双向流式 RPC

基础模板 + 空定义

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

写代码前,建议先将 gRPC Server 的基础模板和接口给空定义出来。若有不清楚可参见上一章节的知识点

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

服务器端流式 RPC,显然是单向流,并代指 Server 为 Stream 而 Client 为普通 RPC 请求

简单来讲就是客户端发起一次普通的 RPC 请求,服务端通过流式响应多次发送数据集,客户端 Recv 接收数据集。大致如图:

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

在 Server,主要留意 stream.Send 方法。它看上去能发送 N 次?有没有大小限制?

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

通过阅读源码,可得知是 protoc 在生成时,根据定义生成了各式各样符合标准的接口方法。最终再统一调度内部的 SendMsg 方法,该方法涉及以下过程:

消息体(对象)序列化压缩序列化后的消息体对正在传输的消息体增加 5 个字节的 header判断压缩+序列化后的消息体总字节长度是否大于预设的 maxSendMessageSize(预设值为 math.MaxInt32),若超出则提示错误写入给流的数据集跟我学 gRPC—3. gRPC Streaming, Client 和 Server

在 Client,主要留意 stream.Recv() 方法。什么情况下 io.EOF ?什么情况下存在错误信息呢?

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

RecvMsg 会从流中读取完整的 gRPC 消息体,另外通过阅读源码可得知:

(1)RecvMsg 是阻塞等待的

(2)RecvMsg 当流成功/结束(调用了 Close)时,会返回 io.EOF

(3)RecvMsg 当流出现任何错误时,流会被中止,错误信息会包含 RPC 错误码。而在

RecvMsg 中可能出现如下错误:

io.EOFio.ErrUnexpectedEOFtransport.ConnectionErrorgoogle.golang/grpc/codes

同时需要注意,默认的 MaxReceiveMessageSize 值为 1024 * 1024 * 4,建议不要超出

运行 stream_server/server.go:

$ go run server.go

运行 stream_client/client.go:

$ go run client.go

2018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 2018

2018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 2019

2018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 2020

2018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 2021

2018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 2022

2018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 2023

2018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 2024

客户端流式 RPC,单向流,客户端通过流式发起多次 RPC 请求给服务端,服务端发起一次响应给客户端,大致如图:

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

多了一个从未见过的方法 stream.SendAndClose,它是做什么用的呢?

在这段程序中,我们对每一个 Recv 都进行了处理,当发现 io.EOF (流关闭) 后,需要将最终的响应结果发送给客户端,同时关闭正在另外一侧等待的 Recv

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

stream.CloseAndRecv 和 stream.SendAndClose 是配套使用的流方法,相信聪明的你已经秒懂它的作用了

重启 stream_server/server.go,再次运行 stream_client/client.go:

stream_client:

$ go run client.go

2018/09/24 16:23:03 resp: pj.name: gRPC Stream Server: Record, pt.value: 1

stream_server:

$ go run server.go

2018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 2018

2018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 2018

2018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 2018

2018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 2018

2018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 2018

2018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 2018

双向流式 RPC,顾名思义是双向流。由客户端以流式的方式发起请求,服务端同样以流式的方式响应请求

首个请求一定是 Client 发起,但具体交互方式(谁先谁后、一次发多少、响应多少、什么时候关闭)根据程序编写的方式来确定(可以结合协程)

假设该双向流是按顺序发送的话,大致如图:

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

还是要强调,双向流变化很大,因程序编写的不同而不同。双向流图示无法适用不同的场景

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

跟我学 gRPC—3. gRPC Streaming, Client 和 Server

重启 stream_server/server.go,再次运行 stream_client/client.go:

stream_server

$ go run server.go

2018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 2018

2018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 2018

2018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 2018

2018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 2018

2018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 2018

2018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 2018

stream_client

$ go run client.go

2018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 0

2018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 1

2018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 2

2018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 3

2018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 4

2018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 5

2018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 6

在本文共介绍了三类流的交互方式,可以根据实际的业务场景去选择合适的方式。会事半功倍哦

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消