3. gRPC 通信模式:一元RPC、服务端流式RPC、客户端流式RPC、双向流式RPC
gRPC 基于 HTTP/2 协议,提供了四种通信模式(Communication Pattern),覆盖了从简单的请求-应答到复杂的流式处理场景。理解这四种模式是设计高性能微服务通信架构的基础,也是后续实施限流、熔断与数据传输优化的前提。
3.1 四种通信模式概述
gRPC 的通信模式由 Protobuf 服务定义中的 rpc 关键字和 stream 修饰符决定。下表从调用方式、数据流向和典型场景三个维度进行对比:
| 模式名称 | 定义语法 | 客户端发送 | 服务端响应 | 典型场景 |
|---|---|---|---|---|
| 一元 RPC | rpc Foo(Request) returns (Response) |
1 条消息 | 1 条消息 | 传统 REST 替代、查询、写入 |
| 服务端流式 RPC | rpc Foo(Request) returns (stream Response) |
1 条消息 | 多条消息(流) | 日志推送、数据订阅、大文件下载 |
| 客户端流式 RPC | rpc Foo(stream Request) returns (Response) |
多条消息(流) | 1 条消息 | 批量上传、传感器数据聚合 |
| 双向流式 RPC | rpc Foo(stream Request) returns (stream Response) |
多条消息(流) | 多条消息(流) | 实时聊天、协同编辑、游戏同步 |
关键认知: 流式 RPC 并非一次性发送所有数据,而是通过 HTTP/2 的帧(Frame)机制持续传输。服务端和客户端各自维护独立的读写循环,这是实现背压(Backpressure)和限流的基础。
3.2 一元 RPC(Unary RPC)
一元 RPC 是最基础的通信模式,与传统的 HTTP REST 调用在语义上最为接近。客户端发送一个请求,服务端返回一个响应,完成后连接关闭。
- 优点: 实现简单,调试方便,适合大多数 CRUD 操作。
- 缺点: 无法处理大体积数据的分批传输,客户端必须等待完整响应。
- 性能关注点: 序列化/反序列化开销、连接池复用、超时配置。
// Protobuf 定义
service UserService {
rpc GetUser (GetUserRequest) returns (GetUserResponse);
}
// 客户端调用(Java 示例)
UserServiceGrpc.UserServiceBlockingStub stub = UserServiceGrpc.newBlockingStub(channel);
GetUserResponse response = stub.getUser(GetUserRequest.newBuilder().setUserId(1001).build());
System.out.println("User name: " + response.getName());
3.3 服务端流式 RPC(Server Streaming RPC)
客户端发送一个请求,服务端以流的形式返回多条消息。客户端通过迭代器或回调逐条读取,直到服务端发送 onCompleted 信号。
- 适用场景: 实时日志流、股票行情推送、大数据集的分页查询。
- 流量控制: 客户端可以通过
FlowControl机制控制接收速率,防止内存溢出。 - 限流策略: 服务端可在流中插入速率限制信号,或直接拒绝新流请求。
// Protobuf 定义
service LogService {
rpc SubscribeLogs (LogRequest) returns (stream LogEntry);
}
// 服务端实现(Go 示例)
func (s *logServer) SubscribeLogs(req *pb.LogRequest, stream pb.LogService_SubscribeLogsServer) error {
for _, entry := range fetchLogs(req.GetLevel()) {
if err := stream.Send(entry); err != nil {
return err
}
time.Sleep(100 * time.Millisecond) // 模拟限速
}
return nil
}
3.4 客户端流式 RPC(Client Streaming RPC)
客户端发送多条消息流,服务端在接收完所有消息后返回一个聚合响应。客户端通过 onNext 发送数据,最后调用 onCompleted 通知服务端结束。
- 适用场景: 批量数据上传、IoT 传感器数据上报、文件分片上传。
- 数据完整性: 服务端通常会在收到
onCompleted后校验数据完整性,再返回结果。 - 限流熔断: 服务端可以设置最大消息数或最大字节数阈值,超出后主动取消流。
// Protobuf 定义
service UploadService {
rpc UploadFile (stream FileChunk) returns (UploadStatus);
}
// 客户端发送(Java 示例)
StreamObserver<UploadStatus> responseObserver = new StreamObserver<>() {
public void onNext(UploadStatus status) {
System.out.println("Upload result: " + status.getSuccess());
}
public void onError(Throwable t) { /* 处理错误 */ }
public void onCompleted() { /* 完成 */ }
};
StreamObserver<FileChunk> requestObserver = stub.uploadFile(responseObserver);
for (FileChunk chunk : splitFile("data.bin")) {
requestObserver.onNext(chunk);
}
requestObserver.onCompleted();
3.5 双向流式 RPC(Bidirectional Streaming RPC)
客户端和服务端可以独立、并发地发送和接收消息流。双方都不需要等待对方完成,实现了真正的全双工通信。
- 适用场景: 实时聊天、多人协作编辑、游戏状态同步、实时音视频信令。
- 架构挑战: 连接管理、消息顺序保证、心跳保活、背压处理。
- 限流熔断: 双向流需要同时监控读写速率,任何一方过载都应触发熔断。
// Protobuf 定义
service ChatService {
rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}
// 服务端双向流处理(Go 示例)
func (s *chatServer) Chat(stream pb.ChatService_ChatServer) error {
for {
msg, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
// 处理消息并广播
response := &pb.ChatMessage{Content: "Echo: " + msg.GetContent()}
if err := stream.Send(response); err != nil {
return err
}
}
}
架构建议: 在微服务架构中,一元 RPC 应作为默认通信方式;服务端流式 RPC 适合数据推送;双向流式 RPC 应谨慎使用,仅在需要低延迟双向交互时引入,并配合连接池和心跳机制。
3.6 四种模式的数据流对比图
下图从数据流向和时序角度,直观对比四种通信模式的核心差异:
3.7 模式选择决策树
在实际架构设计中,可以按照以下决策路径快速选择合适的通信模式:
- 是否需要服务端主动推送?
- 是 → 考虑 服务端流式 RPC 或 双向流式 RPC
- 否 → 进入下一步
- 客户端是否需要发送大量数据?
- 是 → 考虑 客户端流式 RPC
- 否 → 进入下一步
- 是否需要实时双向交互?
- 是 → 选择 双向流式 RPC
- 否 → 选择 一元 RPC
避坑提醒: 不要为了“未来扩展”而盲目使用双向流式 RPC。双向流会显著增加连接管理的复杂度,且对网络稳定性要求更高。在不需要双向推送的场景下,一元 RPC 配合轮询或 Webhook 往往是更稳健的选择。
3.8 四种模式在限流熔断中的差异化处理
不同的通信模式对限流和熔断策略有直接影响。下表总结了各模式下的关键考量:
| 通信模式 | 限流粒度 | 熔断触发条件 | 推荐策略 |
|---|---|---|---|
| 一元 RPC | 请求数/秒(QPS) | 错误率 > 50% 或延迟 > 阈值 | 令牌桶 + 熔断器 |
| 服务端流式 RPC | 流数量 + 消息发送速率 | 流建立失败率 > 30% | 连接池限制 + 速率限制 |
| 客户端流式 RPC | 消息接收速率 + 总数据量 | 内存占用 > 80% 或超时 | 背压 + 窗口大小控制 |
| 双向流式 RPC | 读写双向速率 + 活跃流数 | 任一方速率异常或连接中断 | 双向速率限制 + 心跳检测 |