go的grpc的三种流模式通信

news/2024/11/14 12:58:37/

go的grpc的三种流模式通信

    • 1、grpc通信模式简介
    • 2、stream.proto文件
    • 3、服务端代码 server.go
    • 4、客户端代码client.go
    • 5、测试说明

grpc_1">1、grpc通信模式简介

grpc的数据传输可以分为4种模式:
简单模式 (一元调用)
服务端流模式 (服务端返回实时股票数据给前台)
客户端流模式 (物联网硬件设备向后端发送数据)
双向流模式 (聊天场景)

2、stream.proto文件

syntax = "proto3";option go_package = "./;proto";// grpc的数据传输可以分为4种模式:
// 简单模式(一元调用)、服务端流模式(服务端返回实时股票数据给前台)、客户端流模式(物联网硬件设备向后端发送数据)、双向流模式(聊天场景)service Greeter {rpc GetStream(StreamReqData) returns (stream StreamResData);//  服务端流模式rpc PutStream(stream StreamReqData) returns (StreamResData);//  客户端流模式rpc AllStream(stream StreamReqData) returns (stream StreamResData);//  双向流模式
}// 请求数据结构体
message StreamReqData{string data = 1;
}// 响应数据结构体
message StreamResData{string data = 1;
}

生成客户端代理stub程序、服务端代理stub程序、接口相关代码的命令:
protoc --go_out=. --go-grpc_out=. stream.proto

3、服务端代码 server.go

package mainimport ("Go_Bible/stream_grpc_test/proto""fmt""google.golang.org/grpc""net""sync""time"
)// 端口
const PORT = ":8088"// 自定义服务结构体
type MyServer struct {proto.UnimplementedGreeterServer
}// 实现服务端流模式方法
func (s *MyServer) GetStream(req *proto.StreamReqData, srvStr proto.Greeter_GetStreamServer) error {i := 0for {i++// 向客户端发送响应结构体_ = srvStr.SendMsg(&proto.StreamResData{Data:fmt.Sprintf("%v", time.Now().Unix()),})time.Sleep(time.Second)// 每隔一秒发送1次,总共发送10次if i >= 10 {break}}return nil
}// 实现客户端流模式方法
func (s *MyServer) PutStream(cliStr proto.Greeter_PutStreamServer) error {for {if data, err := cliStr.Recv(); err != nil {fmt.Println("接受客户端的流数据失败:" + err.Error())break}else {fmt.Println("接受到客户端的流数据成功:" + data.Data)}}return nil
}// 实现双向流模式方法
func (s *MyServer) AllStream(allStr proto.Greeter_AllStreamServer) error {wg := sync.WaitGroup{}wg.Add(2)go func() {defer wg.Done()i := 0for {i++// 向客户端发送响应结构体_ = allStr.SendMsg(&proto.StreamResData{Data: fmt.Sprintf("我是服务器 %d", i)})time.Sleep(time.Second)if i >= 10 {break}}}()go func() {defer wg.Done()for {data, _ := allStr.Recv()fmt.Println("服务端接受到客户端的流数据成功:" + data.Data)}}()// 等待相关协程开启调用完成wg.Wait()return nil
}func main() {// 1、监听端口listener, err := net.Listen("tcp", PORT)if err != nil {panic("监听端口失败:" + err.Error())}// 2、创建服务server := grpc.NewServer()// 3、注册服务proto.RegisterGreeterServer(server, &MyServer{})// 4、启动服务err = server.Serve(listener)if err != nil {panic("启动服务失败:" + err.Error())}
}

4、客户端代码client.go

package mainimport ("Go_Bible/stream_grpc_test/proto""context""fmt""google.golang.org/grpc""strconv""sync""time"
)// testGetStream 测试服务端流模式
func testGetStream(client proto.GreeterClient){res, err := client.GetStream(context.Background(), &proto.StreamReqData{Data: "我是客户端"})if err != nil {panic("服务端流模式,从服务端获取数据失败:" + err.Error())}for {data, err := res.Recv()if err != nil {fmt.Println("从服务端获取数据失败:" + err.Error())break}fmt.Println("从服务端获取到数据成功:" + data.Data)}
}// testPutStream 测试客户端流模式
func testPutStream(client proto.GreeterClient){putStrClient, err := client.PutStream(context.Background())if err != nil {panic("客户端流模式,向服务端发送数据失败:" + err.Error())}i := 0for {i++fmt.Println(i)if err = putStrClient.Send(&proto.StreamReqData{Data: strconv.Itoa(i)}); err != nil {fmt.Println("喜爱那个服务端发送数据失败:" + err.Error())break}time.Sleep(time.Second)if i >= 10 {break}}
}// testAllStream 测试双向流模式
func testAllStream(client proto.GreeterClient){allStrClient, err := client.AllStream(context.Background())if err != nil {panic("双向流模式获取失败:" + err.Error())}wg := sync.WaitGroup{}wg.Add(2)// 向服务端发送数据 因为服务端会从客户端源源不断获取数据,因此服务端也不会自动关闭go func() {defer wg.Done()i := 0for {i++err = allStrClient.Send(&proto.StreamReqData{Data: fmt.Sprintf("双向流模式发送的数据:%d", i)})if err != nil {fmt.Println("双向流模式向服务端发送数据失败:" + err.Error())break}if i >= 10 {break}}}()// 从服务端接受数据,因为双向流模式时,客户端需要从服务端源源不断接受数据,因此不会关闭go func() {defer wg.Done()for {data, err := allStrClient.Recv()if err != nil {fmt.Println("双向流模式从服务端接受数据失败:" + err.Error())break}fmt.Println("双向流模式收到服务端消息:" + data.Data)}}()wg.Wait()
}func main() {//	1、拨号conn, err := grpc.Dial("localhost:8088", grpc.WithInsecure())if err != nil {panic("连接失败:" + err.Error())}// 关闭连接defer conn.Close()//2、创建客户端client := proto.NewGreeterClient(conn)/*3、测试服务端流模式*///testGetStream(client)/*4、测试客户端流模式*///testPutStream(client)/*5、测试双向流模式*/testAllStream(client)
}

5、测试说明

先启动服务端server.go
再启动客户端client.go,调用对应函数进行测试


http://www.ppmy.cn/news/1453398.html

相关文章

MySQL——数据库基础

目录 一.数据库的操作 1.显示当前的数据库 2.创建数据库 3.使用数据库 4.删除数据库 一.数据库的操作 1.显示当前的数据库 SHOW DATABASES;2.创建数据库 语法: CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] ..…

redis简介、14条常用的redis命令以及执行结果

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,也可以被视为一个高级的键值存储系统。 与传统的关系型数据库相比,Redis是基于内存的,这使得它具有非常高的读写性能。Redis支持多种数据结构&…

深入解析Floyd Warshall算法:原理、Java实现与优缺点

Floyd Warshall算法的简介 在我们的日常生活中,常常会遇到需要找出两点之间最短路径的问题。比如,从家到公司的最短路线,或者在旅行时,从一个景点到另一个景点的最快路线。 为了解决这类问题,科学家们设计出了许多算法…

算法训练营第55天|LeetCode 392.判断子序列 115.不同的子序列

LeetCode 392.判断子序列 题目链接&#xff1a; LeetCode 392.判断子序列 代码&#xff1a; class Solution { public:bool isSubsequence(string s, string t) {int size_S s.size();int size_T t.size();if(size_S>size_T) return false;int i0,j0;while(i<size_…

vue3--element-plus-抽屉文件上传和富文本编辑器

一、封装组件 article/components/ArticleEdit.vue <script setup> import { ref } from vue const visibleDrawer ref(false)const open (row) > {visibleDrawer.value trueconsole.log(row) }defineExpose({open }) </script><template><!-- 抽…

附录6-4 黑马优购项目-分类和购物车

目录 1 分类 1.1 接口 1.2 窗口限制 1.3 选中状态样式判断 1.4 点击左侧时右侧会到顶点 1.5 源码 2 购物车 2.1 store 2.2 tabBar徽标 2.3 滑动删除 2.4 结算 2.4.1 结算前登录 2.4.2 结算功能 2.5 触发组件事件 2.6 源码 1 分类 分类最上部是…

c++中map与set的基本使用

c中的map容器与set容器 map的所有函数方法及其用法 在C中&#xff0c;std::map 是一个关联容器&#xff0c;它包含可以重复的键值对&#xff08;实际上&#xff0c;std::map中的键是唯一的&#xff09;。每个元素都有一个唯一的键和一个与之关联的值。std::map通常按照其键的…

C++成员初始化列表

我们在类的构造函数中使用成员初始化列表可以带来效率上的提升&#xff0c;那么成员初始化列表在编译后会发生什么就是这篇文章要探究的问题 文章目录 引入成员初始化列表用成员初始化列表优化上面的代码成员初始化列表展开成员初始化列表的潜在危险 参考资料 引入 考虑下面这…