gRPC 提供了四种主要的流模式,分别是简单 RPC(Unary RPC)、服务器流式 RPC(Server Streaming RPC)、客户端流式 RPC(Client Streaming RPC)和双向流式 RPC(Bidirectional Streaming RPC)。这些流模式允许客户端和服务器之间以不同的方式进行数据交换。以下是对这四种流模式的介绍:
介绍
1. 简单 RPC (Unary RPC)
简单 RPC 是最基本的 gRPC 模式。客户端发送一个单一请求,服务器返回一个单一响应。这种模式类似于常规的函数调用。
特点:
- 单次请求和单次响应。
- 客户端发送请求并等待服务器返回响应。
使用场景:
- 适用于大多数常规的远程调用操作,例如获取单个用户信息或提交单个订单。
2. 服务器流式 RPC (Server Streaming RPC)
在服务器流式 RPC 中,客户端发送一个请求,服务器返回一个流式响应。服务器可以向客户端发送多个消息,客户端会按顺序接收这些消息。
特点:
- 客户端发送单次请求。
- 服务器返回多次响应(流式)。
- 服务器可以在单次请求中发送多个消息,客户端按顺序接收。
使用场景:
- 适用于服务器需要发送大量数据或连续数据给客户端的场景,例如实时数据更新、日志流等。
3. 客户端流式 RPC (Client Streaming RPC)
在客户端流式 RPC 中,客户端可以向服务器发送一个流式请求,服务器接收所有请求后,返回一个单一响应。
特点:
- 客户端发送多次请求(流式)。
- 服务器返回单次响应。
- 服务器在接收到所有客户端请求后返回响应。
使用场景:
- 适用于客户端需要连续发送数据到服务器的场景,例如上传文件、连续传输传感器数据等。
4. 双向流式 RPC (Bidirectional Streaming RPC)
双向流式 RPC 是最复杂的流模式,允许客户端和服务器同时进行流式读写。客户端和服务器都可以随时发送和接收消息。消息的顺序由通信双方决定,具有最大的灵活性。
特点:
- 客户端和服务器都可以发送和接收多个消息。
- 双向流式通信,通信双方可以独立地发送和接收数据。
- 消息传输顺序由客户端和服务器控制。
使用场景:
- 适用于需要双向实时通信的场景,例如聊天应用、视频会议、实时游戏等。
代码实现
目录树如下:
.
├── go.mod
├── go.sum
└── stream_grpc
├── client
│ └── client.go
├── proto
│ ├── stream_grpc.pb.go
│ ├── stream.pb.go
│ └── stream.proto
└── server
└── server.go
-
.proto
.代码syntax = "proto3"; //option go_package = "stream_grpc/proto"; service StreamService { // 服务端流模式 rpc GetStream(StreamRequestData) returns (stream StreamResponseData); // 客户端流模式 rpc PostStream(stream StreamRequestData) returns (StreamResponseData); // 双向流模式 rpc AllStream(stream StreamRequestData) returns (stream StreamResponseData); } message StreamRequestData { string data = 1; } message StreamResponseData { string data = 1; }
-
client/client.go
package main import ( "context" "fmt" "go-try/stream_grpc/proto" "google.golang.org/grpc" "sync" "time" ) func main() { conn, err := grpc.NewClient("0.0.0.0:8080", grpc.WithInsecure()) if err != nil { panic(err) } defer conn.Close() cli := proto.NewStreamServiceClient(conn) // 服务端流 res, _ := cli.GetStream(context.Background(), &proto.StreamRequestData{Data: "hello"}) for { recv, err := res.Recv() if err != nil { fmt.Println(err) break } fmt.Println(recv.Data) } // 客户端流 postStream, _ := cli.PostStream(context.Background()) for i := 0; i < 10; i++ { err := postStream.Send(&proto.StreamRequestData{Data: "hello " + fmt.Sprint(i)}) if err != nil { fmt.Println("--", err) break } time.Sleep(time.Second) } // 双向流 allStream, _ := cli.AllStream(context.Background()) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() for { _ = allStream.Send(&proto.StreamRequestData{Data: "你好,我是客户端!"}) time.Sleep(time.Second) } }() go func() { defer wg.Done() for { recv, _ := allStream.Recv() fmt.Println("收到服务端消息: ", recv.Data) } }() wg.Wait() }
-
server/server.go
package main import ( "fmt" "go-try/stream_grpc/proto" "google.golang.org/grpc" "net" "sync" "time" ) type Server struct { proto.UnimplementedStreamServiceServer } // 服务端流 func (s *Server) GetStream(req *proto.StreamRequestData, stream proto.StreamService_GetStreamServer) error { // 模拟返回stream for i := 0; i < 10; i++ { err := stream.Send(&proto.StreamResponseData{Data: req.Data + " " + fmt.Sprint(i)}) if err != nil { return err } time.Sleep(time.Second) } return nil } // 客户端流 func (s *Server) PostStream(cliStream proto.StreamService_PostStreamServer) error { for { recv, err := cliStream.Recv() if err != nil { fmt.Println(err) break } fmt.Println(recv.Data) } return nil } // 双向流 func (s *Server) AllStream(allStream proto.StreamService_AllStreamServer) error { var wg sync.WaitGroup wg.Add(2) // 返回stream go func() { defer wg.Done() for { _ = allStream.Send(&proto.StreamResponseData{Data: "你好!我是服务端!"}) time.Sleep(time.Second) } }() go func() { defer wg.Done() for { recv, _ := allStream.Recv() fmt.Println("收到客户端消息: ", recv.Data) } }() wg.Wait() return nil } func main() { listen, err := net.Listen("tcp", "0.0.0.0:8080") if err != nil { panic("failed to listen" + err.Error()) } server := grpc.NewServer() proto.RegisterStreamServiceServer(server, &Server{}) if err = server.Serve(listen); err != nil { panic("failed to start grpc" + err.Error()) } }
总结
流模式 | 客户端请求次数 | 服务器响应次数 | 描述 |
---|---|---|---|
简单 RPC | 1 | 1 | 单次请求和单次响应 |
服务器流式 RPC | 1 | 多次 | 单次请求和多次响应(流式) |
客户端流式 RPC | 多次 | 1 | 多次请求和单次响应(流式) |
双向流式 RPC | 多次 | 多次 | 多次请求和多次响应(流式) |
通过选择合适的流模式,gRPC 可以灵活地满足不同的应用需求,从简单的请求-响应模型到复杂的实时双向通信。