Golang gRPC 通信模式

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)
}

适用场景

  1. 大部分场景
    对于一次请求只需要返回一个结果的情况,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)
    }
}

服务端流的工作流程

  1. 客户端发送请求:客户端向服务器发送单个请求消息。
  2. 服务器响应流数据:服务器接收请求后,处理请求逻辑并通过流的方式多次发送响应消息给客户端。
  3. 客户端处理响应:客户端持续监听并处理服务器发送的每个响应,直到流结束。
  4. 流结束:服务器发送完所有响应后关闭流,客户端得知流结束后停止接收。

适用场景

服务端流适用于服务器需要向客户端连续发送多条数据的场景,例如:

  • 实时数据传输:如股票价格(会实时变化)。
  • 批量数据传输:如一个大文件的多个分片传输,或者数据库中的大数据集分批发送。
  • 长时间任务的进度推送:服务器执行长时间任务时,可以实时将进度消息推送给客户端。

客户端流模式

特点

  • 客户端发起流式请求,可以连续发送多条消息,直到流结束。
  • 服务器处理完所有请求后返回一个单一的响应。

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 系统中,设备可能持续发送传感器数据,而服务器根据数据实时反馈指令。
    例如智能家居、工业设备监控,服务器可以根据实时监控数据进行控制操作,如温度调节或故障报警,支持智能控制的实时性。

  • 游戏或多媒体流

    在多人在线游戏或流媒体应用中,双向流允许客户端和服务器之间实时交换玩家位置、动作、状态等信息,从而保证游戏的低延迟互动。
    同时,对于互动性强的直播场景,观众可以发送实时反馈或指令,增强互动体验。

  • 远程监控与控制

    对于需要远程控制的场景,例如远程手术、无人机控制、机器人操控等,双向流模式允许操作端和服务器之间持续交换控制和反馈信息。
    实时双向通信能确保操作的精确性和设备的实时响应。

  • 实时数据流处理

    在需要实时处理的数据流应用中,例如风控监测、网络监控系统,双向流模式允许客户端和服务器间持续传输和处理数据。
    服务器可以实时分析、反馈风险状况或异常指标,客户端根据反馈立即采取相应措施。

本文由 上传。


如果您喜欢这篇文章,请点击链接 Golang gRPC 通信模式 查看原文。


您也可以直接访问:https://www.fanfine.cn/blog/249

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇