3. gRPC 通信模式:一元RPC、服务端流式RPC、客户端流式RPC、双向流式RPC的适用场景与对比

gRPC 基于 HTTP/2 协议,定义了四种通信模式(Communication Pattern)。理解这四种模式的区别与适用场景,是进行 gRPC 审计基线设计的基础。不同的模式决定了数据流的走向、并发模型以及安全审计的侧重点。

3.1 四种通信模式概述

gRPC 的通信模式由 .proto 文件中 service 定义的关键字决定。下表从数据流方向、典型应用、审计关注点三个维度进行对比:

模式 关键字 数据流方向 典型应用场景 审计关注点
一元 RPC rpc Foo(Request) returns (Response) 客户端 → 服务端 → 客户端 传统请求-响应、查询、登录验证 请求完整性、响应延迟、单次认证
服务端流式 RPC rpc Foo(Request) returns (stream Response) 客户端 → 服务端 → (多次) → 客户端 实时推送、日志流、监控数据、大文件下载 流中断检测、消息序列号、背压控制
客户端流式 RPC rpc Foo(stream Request) returns (Response) 客户端 → (多次) → 服务端 → 客户端 批量上传、IoT 传感器数据聚合、大文件上传 数据完整性校验、流结束信号、超时控制
双向流式 RPC rpc Foo(stream Request) returns (stream Response) 客户端 ↔ 服务端 (全双工) 实时聊天、游戏同步、协同编辑、金融交易撮合 消息顺序、并发冲突、双向认证、会话管理
审计提示: 在审计基线中,流式 RPC 必须额外检查 grpc-timeout 元数据以及流结束标志(Status.OK 或 Status.CANCELLED),防止资源泄露。

3.2 四种模式的核心区别与适用场景

3.2.1 一元 RPC(Unary RPC)

最基础的通信模式,与传统的 HTTP REST 调用类似。客户端发送一个请求,服务端返回一个响应。适用于大多数同步、低延迟的查询操作。

  • 优点: 实现简单,易于调试,与 HTTP/1.1 语义最接近。
  • 缺点: 无法处理服务端主动推送或客户端批量发送的场景。
  • 审计基线要求: 必须校验请求和响应的 Content-Type 是否为 application/grpc,以及 grpc-status 是否为 0。

3.2.2 服务端流式 RPC(Server Streaming RPC)

客户端发送一个请求,服务端返回一个消息流。服务端可以持续发送消息,直到发送完毕。适用于需要从服务端持续获取数据的场景。

  • 优点: 减少客户端轮询开销,实现实时推送。
  • 缺点: 客户端需要处理流中断和部分消息丢失的情况。
  • 审计基线要求: 审计日志中必须记录流开始时间、结束时间、发送消息总数,并检查是否存在异常中断(如 UNAVAILABLE 状态码)。

3.2.3 客户端流式 RPC(Client Streaming RPC)

客户端发送一个消息流,服务端在接收完所有消息后返回一个响应。适用于客户端需要批量提交数据的场景。

  • 优点: 支持大文件分片上传,降低单次请求负载。
  • 缺点: 服务端必须等待所有消息接收完毕才能处理,延迟较高。
  • 审计基线要求: 必须对每个分片消息进行完整性校验(如 CRC32 或 MD5),并记录分片序号,防止重放攻击。

3.2.4 双向流式 RPC(Bidirectional Streaming RPC)

客户端和服务端可以独立地发送和接收消息流,实现全双工通信。适用于需要实时交互的场景。

  • 优点: 延迟最低,交互最灵活。
  • 缺点: 实现复杂度最高,需要处理并发读写和消息顺序。
  • 审计基线要求: 必须启用 双向 TLS(mTLS),并在审计日志中记录每个消息的 message_id 和 timestamp,以支持会话重建。
安全警告: 双向流式 RPC 由于是全双工通信,容易成为 SSRF(服务端请求伪造) 和 资源耗尽攻击 的目标。审计基线中必须限制每个会话的最大并发消息数,并设置合理的 max_concurrent_streams 参数。

3.3 四种模式的数据流对比图

下图直观展示了四种通信模式中客户端与服务端之间的数据流方向与交互时序:

gRPC 四种通信模式数据流对比 gRPC 四种通信模式数据流对比 客户端 服务端 请求 (1次) 响应 (1次) 一元 RPC 客户端 服务端 请求 (1次) 响应流 (多次) 服务端流式 RPC 客户端 服务端 请求流 (多次) 响应 (1次) 客户端流式 RPC 客户端 服务端 双向流 (全双工) 双向流式 RPC 说明: • 实线箭头表示单次消息,虚线箭头表示流式消息。 • 双向流式 RPC 中,客户端和服务端可以同时发送消息。 • 审计基线需针对不同模式设置不同的流控和日志策略。

3.4 审计基线中的模式选择建议

在制定 gRPC 审计基线时,应根据业务场景选择合适的通信模式,并针对性地配置安全策略:

  1. 高安全性场景(如金融交易、用户认证): 优先使用 一元 RPC,配合 mTLS 和请求签名,审计日志最简洁。
  2. 实时监控场景(如系统指标、日志采集): 使用 服务端流式 RPC,审计基线需关注流中断告警和消息序列号校验。
  3. 批量数据上传场景(如文件上传、传感器数据): 使用 客户端流式 RPC,审计基线必须包含分片校验和超时重试机制。
  4. 低延迟交互场景(如聊天、协同编辑): 使用 双向流式 RPC,审计基线需启用会话级审计,并限制最大并发流数。
实践要点: 在 .proto 文件中定义 RPC 方法时,建议在注释中明确标注该方法的通信模式、预期最大消息大小以及超时时间。这有助于后续审计工具自动生成基线规则。

3.5 代码示例:四种模式的 .proto 定义

以下代码展示了如何在 .proto 文件中定义四种通信模式:

syntax = "proto3";

package audit.example;

// 一元 RPC
service UnaryService {
  rpc GetUser (UserRequest) returns (UserResponse);
}

// 服务端流式 RPC
service ServerStreamService {
  rpc SubscribeEvents (EventRequest) returns (stream EventResponse);
}

// 客户端流式 RPC
service ClientStreamService {
  rpc UploadLogs (stream LogEntry) returns (UploadStatus);
}

// 双向流式 RPC
service BidirectionalService {
  rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}

message UserRequest {
  string user_id = 1;
}

message UserResponse {
  string name = 1;
  string email = 2;
}

message EventRequest {
  string topic = 1;
}

message EventResponse {
  int64 event_id = 1;
  string payload = 2;
}

message LogEntry {
  int64 timestamp = 1;
  string level = 2;
  string message = 3;
}

message UploadStatus {
  bool success = 1;
  int32 total_entries = 2;
}

message ChatMessage {
  string user_id = 1;
  string content = 2;
  int64 timestamp = 3;
}
审计提示: 在审计基线中,应检查 .proto 文件中是否对每个 RPC 方法添加了 option (google.api.http) 注解。如果存在 HTTP 映射,则需要额外审计 REST 网关的安全配置。