gRPC 通信模式
在 proto 的 service 中的方法可以通过以下几种方式来定义,以支持不同类型的通信模式:
单次请求-响应模式
普通的单次请求-响应模式(Unary RPC):
客户端发送一个请求,服务器返回一个响应。
proto写法
服务定义的最基础的写法。
例子:
service ModeService{
// 单次请求-响应模式 - UaryMode
rpc UaryMode(UaryModeReq) returns (UaryModeResp);
}
服务端
// UnaryMode 普通的单次请求-响应模式
func (svc *ModeService) UnaryMode(ctx context.Context, req *pbMode.UnaryModeReq) (*pbMode.UnaryModeResp, error) {
time.Sleep(time.Millisecond) // 增删改查等操作
return &pbMode.UnaryModeResp{
Data: "收到:" + req.GetData(),
}, nil
}
客户端
// 客户端的 Unary 模式
func (sys *ModeSys) UnaryMode() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
req := &pbMode.UnaryModeReq{Data: "hello server"}
client := pbMode.NewModeServiceClient(app.GrpcClientConn)
resp, err := client.UnaryMode(ctx, req)
if err != nil {
panic(err)
}
fmt.Println(resp)
}
适用场景
- 大部分场景
对于一次请求只需要返回一个结果的情况,Unary 模式非常合适。
例如查询一个用户的基本信息、获取订单状态、检查资源的可用性等。
这类操作不涉及大量数据交换或持续交互,因此无需使用流模式。
服务端流模式
特点
- 客户端发送一个请求,服务器返回一个流式的响应数据。
- 服务器可以连续发送多条消息,直到流结束。
proto写法
在服务的方法中,响应的前面添加
stream。
例子:
service ModeService{
// 服务端流 模式
rpc ServerStreamMode(ServerStreamModeReq) returns (stream ServerStreamModeResp);
}
服务端
// ServerStreamMode 服务端流模式
func (svc *ModeService) ServerStreamMode(req *pbMode.ServerStreamModeReq, streaming grpc.ServerStreamingServer[pbMode.ServerStreamModeResp]) error {
// 模拟5次回复,比如返回一个一首诗的
verses := []string{
"《将进酒》 - 李白",
"君不见黄河之水天上来,奔流到海不复回。",
"君不见高堂明镜悲白发,朝如青丝暮成雪。",
"人生得意须尽欢,莫使金樽空对月。",
"天生我材必有用,千金散尽还复来",
}
resp := &pbMode.ServerStreamModeResp{}
var err error
for i, verse := range verses {
resp.Data = fmt.Sprintf("%v,行号:%d,内容:%s", req.Data, i, verse)
if err = streaming.Send(resp); err != nil {
return err
}
}
return nil
}
客户端
// 客户端的服务端流模式
func (sys *ModeSys) ServerStreamingMode() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
req := &pbMode.ServerStreamModeReq{Data: "hello server"}
// 初始化 client,调用服务端流方法获得 ServerStreamingClient
client := pbMode.NewModeServiceClient(app.GrpcClientConn)
streaming, err := client.ServerStreamMode(ctx, req)
if err != nil {
panic(err)
}
// 接收直到服务端发送结束,即 EOF
for {
resp, err := streaming.Recv()
if err == io.EOF {
fmt.Println("EOF")
break
}
if err != nil {
panic(err)
}
fmt.Println("收到:", resp.Data)
}
}
服务端流的工作流程
- 客户端发送请求:客户端向服务器发送单个请求消息。
- 服务器响应流数据:服务器接收请求后,处理请求逻辑并通过流的方式多次发送响应消息给客户端。
- 客户端处理响应:客户端持续监听并处理服务器发送的每个响应,直到流结束。
- 流结束:服务器发送完所有响应后关闭流,客户端得知流结束后停止接收。
适用场景
服务端流适用于服务器需要向客户端连续发送多条数据的场景,例如:
- 实时数据传输:如股票价格(会实时变化)。
- 批量数据传输:如一个大文件的多个分片传输,或者数据库中的大数据集分批发送。
- 长时间任务的进度推送:服务器执行长时间任务时,可以实时将进度消息推送给客户端。
客户端流模式
特点
- 客户端发起流式请求,可以连续发送多条消息,直到流结束。
- 服务器处理完所有请求后返回一个单一的响应。
proto写法
在服务的方法中,请求参数前面添加 stream 。
例子:
service ModeService{
// 客户端流 模式
rpc ClientStreamMode(stream ClientStreamModeReq) returns (ClientStreamModeResp);
}
服务端
// 客户端流模式
func (svc *ModeService) ClientStreamMode(streaming grpc.ClientStreamingServer[pbMode.ClientStreamModeReq, pbMode.ClientStreamModeResp]) error {
num := 0
for {
req, err := streaming.Recv()
if err == io.EOF {
fmt.Println("EOF")
break
}
if err != nil {
return err
}
fmt.Println("序号:", num, " 接收到:", req.Data)
num++
}
return nil
}
客户端
// 客户端的客户端流模式
func (sys *ModeSys) ClientStreamMode() {
req := &pbMode.ClientStreamModeReq{}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
client := pbMode.NewModeServiceClient(app.GrpcClientConn)
streaming, err := client.ClientStreamMode(ctx)
if err != nil {
panic(err)
}
// verses 是分次传输的内容
verses := []string{
"《行路难·其一》 ——李白",
"金樽清酒斗十千,玉盘珍羞直万钱。",
"停杯投箸不能食,拔剑四顾心茫然。",
"欲渡黄河冰塞川,将登太行雪满山。",
"闲来垂钓碧溪上,忽复乘舟梦日边。",
"行路难,行路难,多歧路,今安在?",
"长风破浪会有时,直挂云帆济沧海。",
}
for i, verse := range verses {
req.Data = fmt.Sprintf("序号:%d, 内容:%s", i, verse)
if err = streaming.Send(req); err != nil {
panic(err)
}
}
if err = streaming.CloseSend(); err != nil {
panic("client close failed : " + err.Error())
}
fmt.Println("发送完毕!")
}
适用场景
- 批量数据上传
当客户端需要批量上传大量数据时,客户端流模式可以避免一次性发送所有数据带来的带宽和内存压力。
例如批量上传文件、日志、传感器数据、用户行为记录等,客户端可以逐步发送数据包,服务器在接收完毕后返回处理结果。
- 实时数据汇聚与计算
在需要将客户端的实时数据流汇聚到服务器进行计算的场景下,客户端流模式是理想的选择。
例如 IoT 设备的传感器数据、金融交易数据、股票行情信息等数据流的实时处理,客户端不断发送数据,服务器在接收完全部数据后提供汇总结果或计算结果。
- 长时间收集并处理数据
某些业务场景中需要客户端在一定时间内持续发送数据到服务器,然后进行批量处理。
例如电商应用的用户行为追踪、视频直播过程中的点赞数据收集等,这些场景可以在客户端流结束后,服务端进行集中分析或保存。
- 多步骤输入或累积信息的操作
一些操作需要多步骤输入信息来完成。
例如创建一个复杂的对象时,客户端可以通过流模式依次发送不同属性,服务器在收集所有属性后才创建对象,这样可以更灵活地管理复杂数据结构的生成。
- 节省网络资源的分段发送
如果客户端带宽有限或网络质量不稳定,可以通过客户端流模式将数据分段发送。
这样可以降低单次数据传输的负担,适合网络条件不佳或移动网络下的数据传输,减少丢包、重传的风险。
双向流模式
特点
- 客户端发起流式请求,可以连续发送多条消息,直到流结束。
- 服务器可以连续发送多条消息,直到流结束。
proto写法
在服务的方法中,请求参数、响应的前面添加 stream 。
例子:
service ModeService{
// 双向流 模式
rpc TwoWayStreamMode(stream TwoWayStreamModeReq) returns (stream TwoWayStreamModeResp);
}
服务端
// 双向流模式
func (svc *ModeService) TwoWayStreamMode(streaming grpc.BidiStreamingServer[pbMode.TwoWayStreamModeReq, pbMode.TwoWayStreamModeResp]) error {
for {
// 接收
req, err := streaming.Recv()
if err == io.EOF {
fmt.Println("对方已经关闭")
break
}
if err != nil {
panic("server receive failed " + err.Error())
}
fmt.Println("客户端来信:", req.Data)
// 回复
resp := &pbMode.TwoWayStreamModeResp{
Data: fmt.Sprintf("服务端回信:你好:%s", req.Data),
}
if err = streaming.Send(resp); err != nil {
// 记录日志、重试、重试失败丢到某个重试消息队列什么的。
continue
}
}
return nil
}
客户端
// 客户端的双向流模式
func (sys *ModeSys) TwoWayStreamMode() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
client := pbMode.NewModeServiceClient(app.GrpcClientConn)
streaming, err := client.TwoWayStreamMode(ctx)
if err != nil {
panic(err)
}
// verses 是分次传输的内容
verses := []string{
"《行路难·其一》 ——李白",
"金樽清酒斗十千,玉盘珍羞直万钱。",
"停杯投箸不能食,拔剑四顾心茫然。",
"欲渡黄河冰塞川,将登太行雪满山。",
"闲来垂钓碧溪上,忽复乘舟梦日边。",
"行路难,行路难,多歧路,今安在?",
"长风破浪会有时,直挂云帆济沧海。",
}
req := &pbMode.TwoWayStreamModeReq{}
for i, verse := range verses {
// 发送
req.Data = fmt.Sprintf("序号:%d,内容:%s", i, verse)
if err = streaming.Send(req); err != nil {
// 重试、丢到重试队列等方式
continue
}
// 接收
resp, err := streaming.Recv()
if err == io.EOF {
fmt.Println("服务端已经关闭发送!")
break
}
if err != nil {
panic("receive failed" + err.Error())
}
fmt.Println(resp)
}
if err = streaming.CloseSend(); err != nil {
panic("close send failed" + err.Error())
}
}
适用场景
-
实时聊天或消息系统
在即时通讯应用中,双向流模式允许客户端和服务器之间进行实时消息传输。
用户可以随时发送和接收消息,实现聊天应用中流畅的双向交互效果,尤其适合多人聊天、客服系统等需要实时响应的场景。 -
实时协作应用
在需要多用户协同编辑的应用中,比如共享文档编辑、多人在线绘图或代码协作,双向流模式允许每个用户的更新实时传送到服务器,并同步给其他用户。
这样每个用户的操作都能在其他客户端中即时反映出来,保持协作流畅性。 -
金融交易系统
金融系统中,实时数据的传输非常重要。
例如股票交易、外汇交易中,客户端可以发送交易请求,服务器实时响应并推送市场数据。
双向流支持实时交易、订单处理、价格变化等信息的更新,为金融交易提供低延迟的操作体验。 -
实时物联网(IoT)数据交换
在 IoT 系统中,设备可能持续发送传感器数据,而服务器根据数据实时反馈指令。
例如智能家居、工业设备监控,服务器可以根据实时监控数据进行控制操作,如温度调节或故障报警,支持智能控制的实时性。 -
游戏或多媒体流
在多人在线游戏或流媒体应用中,双向流允许客户端和服务器之间实时交换玩家位置、动作、状态等信息,从而保证游戏的低延迟互动。
同时,对于互动性强的直播场景,观众可以发送实时反馈或指令,增强互动体验。 -
远程监控与控制
对于需要远程控制的场景,例如远程手术、无人机控制、机器人操控等,双向流模式允许操作端和服务器之间持续交换控制和反馈信息。
实时双向通信能确保操作的精确性和设备的实时响应。 -
实时数据流处理
在需要实时处理的数据流应用中,例如风控监测、网络监控系统,双向流模式允许客户端和服务器间持续传输和处理数据。
服务器可以实时分析、反馈风险状况或异常指标,客户端根据反馈立即采取相应措施。


