gRPC入门指南之 双向流式RPC

网络 通信技术
与服务端流式 RPC类似,都只支持单项连续发送数据,今天我们要来学习双向流式 RPC 支持通信双方同时多次发送或接收数据。

[[408239]]

本文转载自微信公众号「Golang来啦」,作者Seekload。转载本文请联系Golang来啦公众号。

你好,我是 Seekload!

前言

前一篇文章我们学习了客户端流式 RPC,客户端多次向服务端发送数据,发送结束之后,由服务端返回一个响应。与服务端流式 RPC类似,都只支持单项连续发送数据,今天我们要来学习双向流式 RPC 支持通信双方同时多次发送或接收数据。如下如所示:

新建并编译proto文件

新建 bidirectional_stream.proto 文件:

  1. syntax = "proto3"
  2.  
  3. package proto; 
  4.  
  5. // 定义流式请求信息 
  6. message StreamRequest{ 
  7.   // 参数类型 参数名称 标识号 
  8.   string data = 1; 
  9.  
  10. // 定义流响应信息 
  11. message StreamResponse{ 
  12.   int32 code = 1; 
  13.   string value = 2; 
  14.  
  15. // 定义我们的服务(可以定义多个服务,每个服务可以定义多个接口) 
  16. service StreamService{ 
  17.   // 双向流RPC,需要在请求、响应数据前加stream 
  18.   rpc Record(stream StreamRequest) returns (stream StreamResponse){}; 

双向流式 RPC,定义方法时需要在请求值和返回值之前加上 stream。

进入 bidirectional_stream.proto 所在的目录,使用如下命令编译文件

  1. protoc --go_out=plugins=grpc:. bidirectional_stream.proto 

执行完成之后会生成 bidirectional_stream.pb.go 文件。

创建server端

  1. package main 
  2.  
  3. import ( 
  4.  pb "go-grpc-example/4-bidirectional_stream_rpc/proto" 
  5.  "google.golang.org/grpc" 
  6.  "io" 
  7.  "log" 
  8.  "net" 
  9.  "strconv" 
  10.  "time" 
  11.  
  12. const ( 
  13.  Address string = ":8000" 
  14.  Network string = "tcp" 
  15.  
  16. // 定义我们的服务 
  17. type StreamService struct{} 
  18.  
  19. // 实现 Record() 方法 
  20. func (s *StreamService) Record(srv pb.StreamService_RecordServer) error { 
  21.  n := 1 
  22.  for { 
  23.   // 接收数据 
  24.   req, err := srv.Recv() 
  25.   if err == io.EOF { 
  26.    return nil 
  27.   } 
  28.   if err != nil { 
  29.    log.Fatalf("stream get from client err: %v", err) 
  30.    return err 
  31.   } 
  32.   // 发送数据 
  33.   err = srv.Send(&pb.StreamResponse{ 
  34.    Code:  int32(n), 
  35.    Value: "This is the " + strconv.Itoa(n) + " message"
  36.   }) 
  37.   if err != nil { 
  38.    log.Fatalf("stream send to client err: %v", err) 
  39.    return err 
  40.   } 
  41.   n++ 
  42.   log.Println("stream get from client: ", req.Data) 
  43.   time.Sleep(1 * time.Second
  44.  } 
  45.  return nil 
  46.  
  47. func main() { 
  48.  // 1.监听端口 
  49.  listener, err := net.Listen(Network, Address) 
  50.  if err != nil { 
  51.   log.Fatalf("listener err: %v", err) 
  52.  } 
  53.  log.Println(Address + " net.Listing..."
  54.  
  55.  // 2.实例化gRPC实例 
  56.  grpcServer := grpc.NewServer() 
  57.  
  58.  // 3.注册我们的服务 
  59.  pb.RegisterStreamServiceServer(grpcServer, &StreamService{}) 
  60.  
  61.  // 4.启动gRPC服务端 
  62.  err = grpcServer.Serve(listener) 
  63.  if err != nil { 
  64.   log.Fatalf("grpc server err: %v", err) 
  65.  } 

在实现的 Record() 方法中,for() 循环里面读取客户端发送的消息并返回一个响应数据。

运行服务端:

  1. go run server.go 
  2.  
  3. 输出: 
  4. :8000  net listening... 

创建client端

  1. package main 
  2.  
  3. import ( 
  4.  "context" 
  5.  pb "go-grpc-example/4-bidirectional_stream_rpc/proto" 
  6.  "google.golang.org/grpc" 
  7.  "io" 
  8.  "log" 
  9.  "strconv" 
  10.  "time" 
  11.  
  12. const Address = ":8000" 
  13.  
  14. func main() { 
  15.  // 1.连接服务端 
  16.  conn, err := grpc.Dial(Address, grpc.WithInsecure()) 
  17.  if err != nil { 
  18.   log.Fatalf("grpc conn err: %v", err) 
  19.  } 
  20.  defer conn.Close() 
  21.  
  22.  // 2.创建gRPC客户端 
  23.  grpcClient := pb.NewStreamServiceClient(conn) 
  24.  
  25.  // 3.调用 Record() 方法获取流 
  26.  stream, err := grpcClient.Record(context.Background()) 
  27.  if err != nil { 
  28.   log.Fatalf("call record err: %v", err) 
  29.  } 
  30.  
  31.  for i := 0; i < 5; i++ { 
  32.   // 4.发送数据 
  33.   err := stream.Send(&pb.StreamRequest{ 
  34.    Data: strconv.Itoa(i), 
  35.   }) 
  36.   if err != nil { 
  37.    log.Fatalf("stream send to server err: %v", err) 
  38.   } 
  39.   // 5.接收服务端发送过来的数据 
  40.   resp, err := stream.Recv() 
  41.   if err == io.EOF { 
  42.    break 
  43.   } 
  44.   if err != nil { 
  45.    log.Fatalf("stream get from server err: %v", err) 
  46.   } 
  47.   log.Printf("stream get from server,code:%v,value:%v", resp.GetCode(), resp.Value) 
  48.   time.Sleep(1 * time.Second
  49.  } 
  50.  // 6.关闭流 
  51.  err = stream.CloseSend() 
  52.  if err != nil { 
  53.   log.Fatalf("close stream err:%v", err) 
  54.  } 

客户端代码,在 for() 循环里面向服务端发送了 5 次消息,并接收服务端返回的数据,5次数据交互之后调用 CloseSend() 关闭流。

运行客户端:

  1. go run client.go 

客户端输出:

  1. stream get from server,code:1,value:This is the 1 message 
  2. stream get from server,code:2,value:This is the 2 message 
  3. stream get from server,code:3,value:This is the 3 message 
  4. stream get from server,code:4,value:This is the 4 message 
  5. stream get from server,code:5,value:This is the 5 message 

服务端输出:

  1. stream get from client:  0 
  2. stream get from client:  1 
  3. stream get from client:  2 
  4. stream get from client:  3 
  5. stream get from client:  4 

观察仔细的同学会注意到,客户端和服务端是交替输出的。

总结

 

这篇文章我们简单介绍了 gRPC 的双向流式 RPC,支持通信双方同时多次发送或接收数据。

 

责任编辑:武晓燕 来源: Golang来啦
相关推荐

2021-06-10 07:49:26

RPCgRPC模式

2021-08-30 07:49:32

NacosSync双向复制

2011-10-18 17:05:45

分布式流式数据库

2024-01-02 12:17:44

Go传统远程

2011-11-29 16:38:58

Knockout

2012-02-29 01:03:10

ubuntuLinux

2011-03-08 09:22:37

2011-05-18 15:15:44

MySQL

2010-08-03 15:19:08

FlexBuilder

2021-03-26 10:31:19

人工智能AIOps

2011-08-03 10:01:20

IOS CoreData 双向关系

2022-04-12 11:46:08

服务gRPC反向代理

2023-02-07 10:40:30

gRPC系统Mac

2023-03-26 23:53:49

TLSgRPC通信

2010-07-20 16:13:25

Perl线程

2011-03-08 16:50:35

2015-09-24 09:54:32

物联网

2011-01-10 13:21:45

linuxsamba

2010-12-31 13:30:12

2019-07-18 07:52:01

路由策略IP路由
点赞
收藏

51CTO技术栈公众号