gRPC 提供了四种主要的流模式,分别是简单 RPC(Unary RPC)、服务器流式 RPC(Server Streaming RPC)、客户端流式 RPC(Client Streaming RPC)和双向流式 RPC(Bidirectional Streaming RPC)。这些流模式允许客户端和服务器之间以不同的方式进行数据交换。以下是对这四种流模式的介绍:
简单 RPC 是最基本的 gRPC 模式。客户端发送一个单一请求,服务器返回一个单一响应。这种模式类似于常规的函数调用。
特点:
使用场景:
在服务器流式 RPC 中,客户端发送一个请求,服务器返回一个流式响应。服务器可以向客户端发送多个消息,客户端会按顺序接收这些消息。
特点:
使用场景:
在客户端流式 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 可以灵活地满足不同的应用需求,从简单的请求-响应模型到复杂的实时双向通信。