环境:
Golang: go1.18.2 windows/amd64
grpc: v1.47.0
protobuf: v1.28.0

完整代码:
https://github.com/WanshanTian/GolangLearning
cd GolangLearning/RPC/gRPC-ClientStreaming

1. 简介

前文【Golang | gRPC】HTTP的连接管理——从HTTP/1.0到HTTP/2.0的演进 简单介绍了gRPC中流模式主要分为客户端流、服务端流、双向流以及流传输模式的优点,下面通过一个demo来说明gRPC客户端流的使用

2. 实践

现有下面一种场景:服务端保存着用户的年龄信息,客户端通过stream多次发送含用户姓名的message,服务端通过stream接收message,一次性返回所有请求用户的年龄和

2.1 proto文件

2.1.1 新建gRPC-ClientStreaming文件夹,使用go mod init初始化,创建pb文件夹,新建query.proto文件

syntax = "proto3";
package pb;
option go_package= ".;pb";// 定义查询服务包含的方法
service Query {// 客户端流模式rpc GetAge (stream userInfo) returns (ageInfo){}
}// 请求用的结构体,包含一个name字段
message userInfo {string name = 1;
}// 响应用的结构体,包含一个age字段
message ageInfo {int32 age = 1;
}

服务端实现一个查询(Query)服务,包含一个方法GetAge。方法GetAge的入参前加关键字stream来表明该方法启用客户端流

2.1.2 在.\gRPC-ClientStreaming\pb目录下使用protoc工具进行编译,在pb文件夹下直接生成.pb.go和_grpc.pb.go文件。关于protoc的详细使用可以查看【Golang | gRPC】使用protoc编译.proto文件

protoc --go_out=./ --go-grpc_out=./ .\query.proto

2.2 grpc.pb.go文件

2.2.1 查看query_grpc.pb.go中生成的客户端流和服务端流的接口定义以及服务端QueryServer服务的定义

// 客户端流
type Query_GetAgeClient interface {Send(*UserInfo) errorCloseAndRecv() (*AgeInfo, error)grpc.ClientStream
}
// 服务端流
type Query_GetAgeServer interface {SendAndClose(*AgeInfo) errorRecv() (*UserInfo, error)grpc.ServerStream
}
//  Query服务的客户端接口
type QueryClient interface {GetAge(ctx context.Context, opts ...grpc.CallOption) (Query_GetAgeClient, error)
}
// Query服务的服务端接口
type QueryServer interface {GetAge(Query_GetAgeServer) errormustEmbedUnimplementedQueryServer()
}
  • 客户端流使用Send发送message,使用CloseAndRecv接收message
  • 客户端GetAge方法的第一个返回值是Query_GetAgeClient,表明生成了一条流,用于发送和接收message;如果有多个方法,则每个方法可以各自生成一条流
  • 服务端GetAge方法的入参是Query_GetAgeServer(流),具体方法需要用户自行实现,可以从流中接收和发送message

2.3 服务端

在gRPC-ClientStreaming目录下新建Server文件夹,新建main.go文件

2.3.1 下面我们通过Query这个结构体具体实现QueryServer接口

// 用户信息
var userinfo = map[string]int32{"foo": 18,"bar": 20,
}type Query struct {pb.UnimplementedQueryServer // 涉及版本兼容
}func (q *Query) GetAge(serverStream pb.Query_GetAgeServer) error {log.Println("start of stream")var names_received []*pb.UserInfofor {userinfoRecv, err := serverStream.Recv()// 待客户端主动关闭流后,退出for循环if err == io.EOF {log.Println("end of the recv direction of the stream")break}log.Printf("The name of user received is %s\n", userinfoRecv.GetName())names_received = append(names_received, userinfoRecv)}// 统计年龄和var ages_sum int32for _, v := range names_received {ages_sum += userinfo[v.GetName()]}// 返回messagelog.Printf("send message about the total of ages:%d ", ages_sum)err := serverStream.SendAndClose(&pb.AgeInfo{Age: ages_sum})if err != nil {log.Panic(err)}log.Println("end of the send direction of the stream")return nil
}
  • 服务端每收到一个message,保存其用户名,直到客户端关闭发送方向的流
  • Recv方法会一直阻塞直到从stream中接收到message,或者直到客户端调用CloseAndRecv方法
  • 当客户端调用CloseAndRecv方法时,服务端调用Recv方法会得到io.EOF返回值
  • 服务端调用SendAndClose方法发送响应message并关闭发送方向的流

2.3.2 服务注册并启动

func main() {// 创建socket监听器listener, _ := net.Listen("tcp", ":1234")// new一个gRPC服务器,用来注册服务grpcserver := grpc.NewServer()// 注册服务方法pb.RegisterQueryServer(grpcserver, new(Query))// 开启gRPC服务_ = grpcserver.Serve(listener)
}

使用RegisterQueryServer这个方法向gRPC服务器里注册QueryServer

2.4 客户端

在gRPC-ClientStreaming目录下新建Client文件夹,新建main.go文件

2.4.1 先建立无认证的连接,生成Client,然后通过方法GetAge返回对应的流,最后通过流进行message的收发

func main() {//建立无认证的连接conn, err := grpc.Dial(":1234", grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Panic(err)}defer conn.Close()client := pb.NewQueryClient(conn)//返回GetAge方法对应的流queryStream, _ := client.GetAge(context.Background())// 向stream中发送message_ = queryStream.Send(&pb.UserInfo{Name: "foo"})time.Sleep(time.Second)_ = queryStream.Send(&pb.UserInfo{Name: "bar"})time.Sleep(time.Second)// 发送两次数据后主动关闭流并等待接收来自server端的messageages_sum, err := queryStream.CloseAndRecv()if err != nil {log.Println(err)}fmt.Printf("The total of ages of foo and bar is %d", ages_sum.GetAge())
}
  • 客户端通过CloseAndRecv方法主动关闭发送方向的流同时等待接收来自服务端的message

运行结果如下:

3. 总结

  • 客户端通过Send方法多次发送message,通过CloseAndRecv方法主动关闭发送方向的流同时等待接收来自服务端的message
  • 服务端通过Recv方法多次接收message,通过SendAndClose方法发送响应message并关闭发送方向的流

【Golang | gRPC】gRPC-Client Streaming客户端流实战相关推荐

  1. java版gRPC实战之四:客户端流

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos <java版gRPC实战>全系列链接 用p ...

  2. 使用Golang搭建gRPC服务提供给.NetCore客户端调用

    gRPC概述 RPC 说到gRPC就不得不提RPC,所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,简单点来说就是我A机器 ...

  3. gRPC的通信方式-客户端流式、服务端流式、双向流式在Java的调用示例

    场景 gPRC简介以及Java中使用gPRC实现客户端与服务端通信(附代码下载): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/ ...

  4. grpc(3):使用 golang 开发 grpc 服务端和client

    1,关于grpc-go golang 能够能够做grpc的服务端和client. 官网的文档: http://www.grpc.io/docs/quickstart/go.html https://g ...

  5. 用Golang构建gRPC服务

    本教程提供了Go使用gRPC的基础教程 在教程中你将会学到如何: 在.proto文件中定义一个服务. 使用protocol buffer编译器生成客户端和服务端代码. 使用gRPC的Go API为你的 ...

  6. Go语学习笔记 - grpc server/client protobuf | 从零开始Go语言

    目录 创建Proto文件 生成proto文件对应的go文件 创建服务结构体 创建客户端测试 小结 学习笔记,写到哪是哪. 上一篇是写的redis操作来着,最近主要研究了一下grpc. 在玩grpc的过 ...

  7. 使用Golang搭建gRPC服务提供给.NetCore调用

    文章目录 gRPC概述 RPC gRPC又是什么呢 安装 Golang IDE(Goland) Protocol Buffer 下载Protocal Buffer 配置Protocal Buffer编 ...

  8. go get google.golang.org/grpc 解决办法汇总

    go get -u google.golang.org/grpc 提示网络不通 package google.golang.org/grpc: unrecognized import path &qu ...

  9. golang安装grpc,timeout问题

    官方安装命令: go get google.golang.org/grpc 是安装不起的,会报: package google.golang.org/grpc: unrecognized import ...

最新文章

  1. 一文看尽 27 篇 CVPR 2021 2D 目标检测论文
  2. 未分配内存的指针导致段错误
  3. Receiver ED、Link quality indicator (LQI)、Clear channel assessment (CCA)究竟是什么?802.15.4标准
  4. 工作53:$router问题
  5. 华为方舟编译器正式开源,采用自主平台托管
  6. saltstack event 实践
  7. ASP.NET页面间的传值的几种方法
  8. hdu1023-----卡特兰数
  9. pt100铂电阻型号_铂热电阻型号及铂热电阻接线方法
  10. MySQL-存储表情字符
  11. PMU电池管理配置与io-domain电源域
  12. HTML网页设计期末课程大作业~仿腾讯游戏官网设计与实现(HTML+CSS+JavaScript)
  13. Vue 可暂停计时器
  14. Typora Emoji图标
  15. perl中grep用法总结 z
  16. 10 Three.js使用orbit controls插件(轨道控制)来控制模型交互动作
  17. MMPV-上月账期关闭,当月账期打开
  18. suse linux 单用户,如何在 SUSE 12 Linux 中进入单用户模式?
  19. SEO站群外链建设批量管理
  20. 2008最新全套计算机毕业设计

热门文章

  1. 努比亚牵手天威诚信,共同打造多域名网站安全解决方案
  2. 请大家协助申诉努比亚利用开源软件申请的专利无效
  3. Vue项目点击刷新页面的三种方式
  4. SGM2019 简介
  5. Slf4j的使用与配置,Lombok Slf4j
  6. 安装Windows/Ubuntu双系统找不到磁盘分区解决办法
  7. 前端和后端之间有什么区别,应该如何选择?
  8. Web前端第一次作业——html表格
  9. Linux服务器安装蒲公英
  10. matlab仿真Gz转换为差分方程,怎么用matlab把传递函数转成差分方程