文档
gRPC 例程:Go 语言四种通信模式
目标
用 Go 实现 gRPC 的四种通信模式:Unary、Server Streaming、Client Streaming、Bidirectional Streaming。
Proto 定义
syntax = "proto3";
package calculator;
option go_package = "example.com/grpc-demo/calculator";
service Calculator {
// 一元调用
rpc Add(AddRequest) returns (AddResponse);
// 服务端流:客户端发一次,服务端发多次
rpc Fibonacci(FibRequest) returns (stream FibResponse);
// 客户端流:客户端发多次,服务端发一次
rpc Average(stream AvgRequest) returns (AvgResponse);
// 双向流:双方自由发送
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
message AddRequest { int32 a = 1; int32 b = 2; }
message AddResponse { int32 result = 1; }
message FibRequest { int32 n = 1; }
message FibResponse { int64 value = 1; }
message AvgRequest { int32 num = 1; }
message AvgResponse { double average = 1; }
message ChatMessage { string user = 1; string text = 2; }
服务端实现
package main
import (
"context"
"io"
"log"
"net"
pb "example.com/grpc-demo/calculator"
"google.golang.org/grpc"
)
type server struct {
pb.UnimplementedCalculatorServer
}
// Unary
func (s *server) Add(ctx context.Context, req *pb.AddRequest) (*pb.AddResponse, error) {
return &pb.AddResponse{Result: req.A + req.B}, nil
}
// Server Streaming
func (s *server) Fibonacci(req *pb.FibRequest, stream pb.Calculator_FibonacciServer) error {
var a, b int64 = 0, 1
for i := int32(0); i < req.N; i++ {
stream.Send(&pb.FibResponse{Value: a})
a, b = b, a+b
}
return nil
}
// Client Streaming
func (s *server) Average(stream pb.Calculator_AverageServer) error {
var sum, count int32
for {
req, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&pb.AvgResponse{
Average: float64(sum) / float64(count),
})
}
if err != nil { return err }
sum += req.Num
count++
}
}
// Bidirectional Streaming
func (s *server) Chat(stream pb.Calculator_ChatServer) error {
for {
msg, err := stream.Recv()
if err == io.EOF { return nil }
if err != nil { return err }
log.Printf("[%s]: %s", msg.User, msg.Text)
stream.Send(&pb.ChatMessage{
User: "Server",
Text: "收到: " + msg.Text,
})
}
}
func main() {
lis, _ := net.Listen("tcp", ":50051")
s := grpc.NewServer()
pb.RegisterCalculatorServer(s, &server{})
log.Println("gRPC 服务启动在 :50051")
s.Serve(lis)
}
运行步骤
# 生成代码
protoc --go_out=. --go-grpc_out=. calculator.proto
# 启动服务端
go run server/main.go
# 启动客户端测试(需编写 client.go)
go run client/main.go
运行结果示例
2024/01/01 gRPC 服务启动在 :50051
[Alice]: Hello!
[Bob]: Hi there!
四种模式对比
| 模式 | 客户端 | 服务端 | 典型场景 |
|---|---|---|---|
| Unary | 1 请求 → 1 响应 | 1 请求 → 1 响应 | 简单查询 |
| Server Stream | 1 请求 → 流式接收 | 1 请求 → 流式发送 | 日志订阅 |
| Client Stream | 流式发送 → 1 响应 | 流式接收 → 1 响应 | 文件上传 |
| Bidi Stream | 自由收发 | 自由收发 | 聊天、实时协作 |