grpc:流式 RPC

流式Rpc

流式 RPC 允许客户端和服务端之间建立一个持续的数据通道, 可以双向传输数据流,常用于处理大量数据和实现双向通讯的场。本文将简介流式Rpc的三种类型即: 客户端流式、服务端流式、双向流式。

接着我们分别通过一些例子(1、大文件上传 2、广播消息、3、多人聊天)的例子来实践。

定义 proto

首先,创建一个greeter.proto

proto 复制代码
service Greeter {
  // hello
  rpc SayHello (HelloRequest) returns (HelloReply) {
  }
  // 上传文件
  rpc UploadFile(stream FileChunk) returns (UploadReply);
  // 广播
  rpc Broadcast(BroadcastRequest) returns (stream Notice);
  // 聊天
  rpc ChatStream(stream ChatRequest) returns (stream ChatReply);
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

message FileChunk {
  bytes data = 1;
}

message UploadReply {
  string message = 1;
}

message BroadcastRequest {
}

message Notice {
  string content = 1;
}

message ChatRequest {
  string message = 1;
}

message ChatReply {
  string message = 1;
} 

生成对应的go文件,命令参考

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative greeter.prot o

客户端流式 RPC

客户端使用提供的流编写一系列消息并将其发送到服务器。客户端完成编写消息后,它会等待服务器读取所有消息并返回其响应。您可以通过将关键字放在请求 stream类型之前来指定客户端流式方法。 客户端代码如下:

go 复制代码
func UploadFile(client pb.GreeterClient, filePath string) error {
    stream, err := client.UploadFile(context.Background())
    if err != nil {
       return err
    }

    file, err := os.Open(filePath)
    if err != nil {
       return err
    }
    defer file.Close()

    buf := make([]byte, 1024)
    for {
       n, err := file.Read(buf)
       if err == io.EOF {
          break
       }
       if err != nil {
          return err
       }
       if err := stream.Send(&pb.FileChunk{Data: buf[:n]}); err != nil {
          return err
       }
    }

    resp, err := stream.CloseAndRecv()
    if err != nil {
       return err
    }

    fmt.Println(resp.Message)
    return nil
}

greeter.proto的UploadFile所示,rpc的请求为'stream FileChunk',例中模拟将文件分块(文中为1k) 通过 stream传输。

服务端流式 RPC

服务器端流式 RPC ,客户端向服务器发送请求并获取流以读取一系列消息。客户端从返回的流中读取,直到没有更多消息。如您在示例中所见,您可以通过将关键字放在响应 stream类型 之前来指定服务器端流式方法。 服务端代码如下

go 复制代码
func (s *server) Broadcast(in *pb.BroadcastRequest, stream pb.Greeter_BroadcastServer) error {
    // 模拟广播消息
    notices := []string{"Notice 1", "Notice 2", "Notice 3"}
    for _, notice := range notices {
       log.Printf("notice: %v", notice)
       if err := stream.Send(&pb.Notice{Content: notice}); err != nil {
          return status.Errorf(codes.Internal, "Failed to send notice: %v", err)
       }
    }
    return nil
}

如greeter.proto的Broadcast 所示,rpc的返回为'stream Notice'示例中,例中模拟了一个广播消息的场景,将消息列表中的的消息依次 stream.Send 方法将每个消息发送给客户端。

双向流式 RPC

双向流式 RPC ,双方使用读写流发送一系列消息。两个流独立运行,因此客户端和服务器可以按任意顺序进行读写:例如,服务器可以等待接收所有客户端消息后再写入响应,也可以交替读取消息然后写入消息,或者进行其他读写组合。每个流中的消息顺序都会保留。您可以通过将关键字放在请求stream 和响应之前来指定此类方法。

客户端代码如下:

go 复制代码
// 调用 ChatStream
stream, err := c.ChatStream(context.Background())
if err != nil {
    log.Fatalf("could not start chat stream: %v", err)
}

// 发送消息并等待响应
messages := []string{"Hello", "How are you?", "This is fun!"}
for _, msg := range messages {
    if err := stream.Send(&pb.ChatRequest{Message: msg}); err != nil {
       log.Fatalf("could not send message: %v", err)
    }

    resp, err := stream.Recv()
    if err == io.EOF {
       break
    }
    if err != nil {
       log.Fatalf("could not receive message: %v", err)
    }
    fmt.Println("Received:", resp.GetMessage())
}

if err := stream.CloseSend(); err != nil {
    log.Fatalf("could not close send: %v", err)
}

服务端代码如下:

go 复制代码
func (s *server) ChatStream(stream pb.Greeter_ChatStreamServer) error {
    for {
       req, err := stream.Recv()
       if err == io.EOF {
          return nil
       }
       if err != nil {
          return err
       }

       // 处理收到的消息
       log.Printf("Received message: %s", req.GetMessage())

       // 发送响应消息
       if err := stream.Send(&pb.ChatReply{Message: "Server: " + req.GetMessage()}); err != nil {
          return err
       }
    }
}

如greeter.proto的 ChatStream 所示,例中模拟了一个实时聊天的场景 ,客户端和服务端通过stream 互发送聊天消息。

参考

相关推荐
虫小宝3 分钟前
Spring Boot与Jenkins的集成
spring boot·后端·jenkins
java66666888841 分钟前
深入理解Spring Boot中的配置加载顺序
java·spring boot·后端
春山之外1 小时前
基于IIS的Windows系统Django项目本地部署
后端·python·django·iis·服务器部署
空青7261 小时前
ChatGPT在Java后端开发中的应用与影响
java·开发语言·人工智能·后端·神经网络·机器学习·chatgpt
冯宝宝^1 小时前
图书管理系统
服务器·数据库·vue.js·spring boot·后端
java6666688882 小时前
深入理解Spring Boot中的容器与依赖注入
java·spring boot·后端
u0104058362 小时前
Spring Boot中的依赖注入和控制反转
java·spring boot·后端
randy.lou2 小时前
GRPC使用之ProtoBuf
java·rpc
虫小宝2 小时前
解决Spring Boot中的安全漏洞与防护策略
java·spring boot·后端
test6382 小时前
使用ThreadLocal存储用户登录信息
java·后端·面试