grpc流模式的实例
grpc中的stream,srteam顾名思义就是一种流,可以源源不断的推送数据,或者服务端和客户端长时间数据交互。
grpc的四种数据流
- 简单模式 : 即客户端发起一次请求,服务端响应一个数据; 在proto3中不用指定 stream。
- 服务端数据流模式: 这种模式是客户端发起一次请求,服务端返回一段连续的数据流。在proto3中如
rpc ServerMod (RequestData) returns (stream ResponseData);
- 客户端数据流模式: 与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。在proto3中如
rpc ClientMod (stream RequestData) returns (ResponseData);
- 双向数据流: 客户端和服务端都可以向对方发送数据流,这个时候双方的数据可以同时互相发送,也就是可以实现实时交互。 在proto3中如
rpc AllMod (stream RequestData) returns (stream ResponseData);
本次实例用的 proto3
syntax = "proto3";
option go_package = "./;pb";
service Mod {
rpc ServerMod (RequestData) returns (stream ResponseData); //服务端流模式,拉消息
rpc ClientMod (stream RequestData) returns (ResponseData); //客户端流模式,推消息
rpc AllMod (stream RequestData) returns (stream ResponseData); //双向流模式,能推能拉
}
message RequestData {
string data = 1;
}
message ResponseData {
string data = 1;
}
Server端实现
const port = 8082
type server struct{}
// 服务端流模式,拉消息
func (s *server) ServerMod(req *pb.RequestData, res pb.Mod_ServerModServer) error {
i := 0
for {
i++
//业务代码
_ = res.Send(&pb.ResponseData{
Data: fmt.Sprintf("这是发给%s的数据流", req.Data),
})
time.Sleep(time.Second * 1)
if i > 10 {
break
}
}
return nil
}
// 客户端流模式,推消息
func (s *server) ClientMod(cliStr pb.Mod_ClientModServer) error {
for {
//业务代码
res, err := cliStr.Recv()
if err != nil {
fmt.Println("本次客户端流数据发送完了:",err)
break
}
fmt.Println("客户端发来消息:",res.Data)
}
return nil
}
// 双向流模式,能推能拉
func (s *server) AllMod(allStr pb.Mod_AllModServer) 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(&pb.ResponseData{
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中
pb.RegisterModServer(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()
}
Client端实现
package main
import (
"context"
"fmt"
"go_script/grpc_3/pb"
"google.golang.org/grpc"
"sync"
"time"
)
var rpc pb.ModClient
// 服务端流模式
func serverStreamDemo() {
res,err:=rpc.ServerMod(context.Background(),&pb.RequestData{Data: "服务端流模式"})
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.ClientMod(context.Background())
if err != nil {
panic("rpc请求错误:" + err.Error())
}
i := 0
for {
i++
_ = cliStr.Send(&pb.RequestData{
Data: "客户端流模式",
})
time.Sleep(time.Second * 1)
if i > 10 {
break
}
}
}
// 双向流模式
func clientAndServerStreamDemo() {
allStr, _ := rpc.AllMod(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(&pb.RequestData{
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 = pb.NewModClient(conn) //初始化
serverStreamDemo() //服务端流模式
clientStreamDemo() //客户端流模式
clientAndServerStreamDemo() // 双向流模式
}
func main() {
start()
}
最后
可以在实际业务中灵活使用,根据漫哥的经验可以按照需求进行设计数据在各个子服务中的流向;