Golang流模式之grpc的四种数据流


Posted in Golang onApril 13, 2022

1. 什么是数据流

grpc中的stream,srteam顾名思义就是一种流,可以源源不断的推送数据,很适合传输一些大数据,或者服务端和客户端长时间数据交互,比如客户端可以向服务端订阅一个数据,服务端就可以利用stream,源源不断地推送数据。

底层还原成socket编程

2. grpc的四种数据流

1.简单模式
2.服务端数据流模式(Server-side streaming RPC)
3.客户端数据流模式(Client-side streaming RPC)
4.双向数据流模式(Bidirectional streaming RPC)

2.1 简单模式

这种模式最为传统,即客户端发起一次请求,服务端响应一个数据,这和大家平时熟悉的RPC没有什么大的区别,上两篇中介绍此模式。

2.2 服务端数据流模式

这种模式是客户端发起一次请求,服务端返回一段连续的数据流。典型的例子是客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端

2.3 客户端数据流模式

与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。典型的例子是物联网终端向服务器报送数据。

2.4 双向数据流

顾名思义,这是客户端和服务端都可以向对方发送数据流,这个时候双方的数据可以同时互相发送,也就是可以实现实时交互。典型的例子是聊天机器人。

3. 上代码

3.1 代码目录

Golang流模式之grpc的四种数据流

3.2 编写stream.proto文件

stream是常量,写在哪一边,哪一边就是数据流
syntax = "proto3";

option go_package = "./;proto";

service Greeter {
    // 定义方法,stream是常量,流模式
    rpc ServerStream (StreamRequestData) returns (stream StreamResponseData);      //服务端流模式,拉消息
    rpc ClientStream (stream StreamRequestData) returns (StreamResponseData);      //客户端流模式,推消息
    rpc AllStream (stream StreamRequestData) returns (stream StreamResponseData);  //双向流模式,能推能拉
}

message StreamRequestData {
    string data = 1; //编号
}

message StreamResponseData {
    string data = 1; //编号
}
生成go的protobuf文件命令:
cd到proto目录下
命令:protoc -I . hello.proto   --go_out=plugins=grpc:.

3.3 编写server文件

package main

import (
	"file_test/grpc_go_stream/proto"
	"fmt"
	"net"
	"sync"
	"time"

	"google.golang.org/grpc"
)

const port = 8082

type server struct{}

func (s *server) ServerStream(req *proto.StreamRequestData, res proto.Greeter_ServerStreamServer) error {
	i := 0
	for {
		i++
		//业务代码
		_ = res.Send(&proto.StreamResponseData{
			Data: fmt.Sprintf("这是发给%s的数据流", req.Data),
		})
		time.Sleep(time.Second * 1)
		if i > 10 {
			break
		}
	}
	return nil
}
func (s *server) ClientStream(cliStr proto.Greeter_ClientStreamServer) error {
	for {
		//业务代码
		res, err := cliStr.Recv()
		if err != nil {
			fmt.Println("本次客户端流数据发送完了:",err)
			break
		}
		fmt.Println("客户端发来消息:",res.Data)
	}
	return nil
}
func (s *server) AllStream(allStr proto.Greeter_AllStreamServer) error {
	wg:=sync.WaitGroup{}
	wg.Add(2)
	//接受客户端消息的协程
	go func() {
		defer wg.Done()
		for  {
			//业务代码
			res, err := allStr.Recv()
			if err != nil {
				fmt.Println("本次客户端流数据发送完了:",err)
				break
			}
			fmt.Println("收到客户端发来消息:",res.Data)
		}
	}()

	//发送消息给客户端的协程
	go func() {
		defer wg.Done()
		i := 0
		for {
			i++
			//业务代码
			_ = allStr.Send(&proto.StreamResponseData{
				Data: fmt.Sprintf("这是发给客户端的数据流"),
			})
			time.Sleep(time.Second * 1)
			if i > 10 {
				break
			}
		}
	}()
	wg.Wait()
	return nil
}

// 启动
func start() {
	// 1.实例化server
	g := grpc.NewServer()
	// 2.注册逻辑到server中
	proto.RegisterGreeterServer(g, &server{})
	// 3.启动server
	lis, err := net.Listen("tcp", "127.0.0.1:8082")
	if err != nil {
		panic("监听错误:" + err.Error())
	}
	err = g.Serve(lis)
	if err != nil {
		panic("启动错误:" + err.Error())
	}

}

func main() {
	start()
}

3.4 编写client文件

package main

import (
	"context"
	"file_test/grpc_go_stream/proto"
	"fmt"
	"sync"
	"time"

	"google.golang.org/grpc"
)

var rpc proto.GreeterClient

func serverStreamDemo()  {
	//服务端流模式
	res,err:=rpc.ServerStream(context.Background(),&proto.StreamRequestData{Data: "jeff"})
	if err != nil {
		panic("rpc请求错误:"+err.Error())
	}
	for  {
		data,err:=res.Recv() //
		if err != nil {
			fmt.Println("客户端发送完了:",err)
			return
		}
		fmt.Println("客户端返回数据流值:",data.Data)
	}
}

func clientStreamDemo()  {
	//客户端流模式
	cliStr, err := rpc.ClientStream(context.Background())
	if err != nil {
		panic("rpc请求错误:" + err.Error())
	}
	i := 0
	for {
		i++
		_ = cliStr.Send(&proto.StreamRequestData{
			Data: "jeff",
		})
		time.Sleep(time.Second * 1)
		if i > 10 {
			break
		}
	}
}

func clientAndServerStreamDemo()  {
	//双向流模式
	allStr, _ := rpc.AllStream(context.Background())
	wg := sync.WaitGroup{}
	wg.Add(1)
	//接受服务端消息的协程
	go func() {
		defer wg.Done()
		for {
			//业务代码
			res, err := allStr.Recv()
			if err != nil {
				fmt.Println("本次服务端流数据发送完了:", err)
				break
			}
			fmt.Println("收到服务端发来消息:", res.Data)
		}
	}()

	//发送消息给服务端的协程
	go func() {
		defer wg.Done()
		i := 0
		for {
			i++
			//业务代码
			_ = allStr.Send(&proto.StreamRequestData{
				Data: fmt.Sprintf("这是发给服务端的数据流"),
			})
			time.Sleep(time.Second * 1)
			if i > 10 {
				break
			}
		}
	}()
	wg.Wait()
}

// 启动
func start() {
	conn, err := grpc.Dial("127.0.0.1:8082", grpc.WithInsecure())
	if err != nil {
		panic("rpc连接错误:" + err.Error())
	}
	defer conn.Close()
	rpc = proto.NewGreeterClient(conn) //初始化

	serverStreamDemo() //服务端流模式

	clientStreamDemo()  //客户端流模式

	clientAndServerStreamDemo() // 双向流模式
}

func main() {
	start()
}

以上就是go实现grpc四种数据流模式的详细内容!

Golang 相关文章推荐
Go各时间字符串使用解析
Apr 02 Golang
Go语言带缓冲的通道实现
Apr 26 Golang
Go语言切片前或中间插入项与内置copy()函数详解
Apr 27 Golang
Go语言中break label与goto label的区别
Apr 28 Golang
golang 实现Location跳转方式
May 02 Golang
关于golang高并发的实现与注意事项说明
May 08 Golang
golang中的并发和并行
May 08 Golang
go xorm框架的使用
May 22 Golang
Go 通过结构struct实现接口interface的问题
Oct 05 Golang
Go语言基础切片的创建及初始化示例详解
Nov 17 Golang
Go web入门Go pongo2模板引擎
May 20 Golang
Golang数据类型和相互转换
Apr 12 #Golang
Go语言的协程上下文的几个方法和用法
Apr 11 #Golang
Golang 1.18 多模块Multi-Module工作区模式的新特性
Apr 11 #Golang
golang三种设计模式之简单工厂、方法工厂和抽象工厂
Golang原生rpc(rpc服务端源码解读)
Apr 07 #Golang
Go并发4种方法简明讲解
Go归并排序算法的实现方法
Apr 06 #Golang
You might like
ThinkPHP3.1新特性之对分组支持的改进与完善概述
2014/06/19 PHP
ThinkPHP页面跳转success与error方法概述
2014/06/25 PHP
php实现数组重复数字统计实例
2018/09/30 PHP
php写入mysql中文乱码的实例解决方法
2019/09/17 PHP
Laravel中validation验证 返回中文提示 全局设置的方法
2019/09/29 PHP
jquery 获取json数据实现代码
2009/04/27 Javascript
js focus不起作用的解决方法(主要是因为dom元素是否加载完成)
2010/11/05 Javascript
jquery动画1.加载指示器
2012/08/24 Javascript
Jquery操作radio的简单实例
2014/01/06 Javascript
JavaScript中使用Object.prototype.toString判断是否为数组
2015/04/01 Javascript
jquery中ajax跨域方法实例分析
2015/12/18 Javascript
在AngularJS中如何使用谷歌地图把当前位置显示出来
2016/01/25 Javascript
jQuery实现的分子运动小球碰撞效果
2016/01/27 Javascript
基于js中的原型、继承的一些想法
2016/08/10 Javascript
手机端 HTML5使用photoswipe.js仿微信朋友圈图片放大效果
2016/08/25 Javascript
HTML5 实现的一个俄罗斯方块实例代码
2016/09/19 Javascript
详解jQuery的Cookie插件
2016/11/23 Javascript
vue-cli如何快速构建vue项目
2017/04/26 Javascript
使用prop解决一个checkbox选中后再次选中失效的问题
2017/07/05 Javascript
Vue通过ref父子组件拿值方法
2018/09/12 Javascript
JavaScript实现动态生成表格
2020/08/02 Javascript
Python内置的字符串处理函数整理
2013/01/29 Python
Python 列表排序方法reverse、sort、sorted详解
2016/01/22 Python
TensorFlow的权值更新方法
2018/06/14 Python
Python中url标签使用知识点总结
2020/01/16 Python
浅析matlab中imadjust函数
2020/02/27 Python
科颜氏加拿大官方网站: Kiehl’s加拿大
2016/08/16 全球购物
英国文具、办公用品和科技商店:Ryman
2018/09/27 全球购物
招商业务员岗位职责
2013/12/16 职场文书
艺术学院毕业生自荐信
2014/07/05 职场文书
教师个人事迹材料
2014/12/17 职场文书
党支部承诺书
2015/01/20 职场文书
2015年新农合工作总结
2015/03/30 职场文书
《家庭教育》读后感3篇
2019/12/18 职场文书
【海涛解说】史上最给力比赛,挑战DOTA极限
2022/04/01 DOTA
SQL SERVER中的流程控制语句
2022/05/25 SQL Server