2. gRPC 基础回顾:Protocol Buffers 定义、一元RPC、服务端流式RPC、客户端流式RPC、双向流式RPC

在深入事务边界之前,必须牢固掌握 gRPC 的通信基石。本章将系统回顾 Protocol Buffers 的定义语法以及四种 RPC 模式,为后续讨论分布式事务中的消息流、状态同步与边界划分建立统一的技术语境。

2.1 Protocol Buffers 定义与核心语法

Protocol Buffers(protobuf)是 gRPC 的接口定义语言(IDL)与序列化协议。一个典型的 .proto 文件包含消息结构、字段编号、字段类型以及服务接口声明。

关键设计原则: protobuf 使用二进制编码,体积小、解析快;字段编号(field number)一旦发布不可修改,否则会导致旧版本兼容性断裂。

2.1.1 消息定义示例

syntax = "proto3";

package transaction.v1;

// 订单请求
message OrderRequest {
  string order_id = 1;
  string user_id = 2;
  double amount = 3;
  int32 quantity = 4;
  map<string, string> metadata = 5;
}

// 订单响应
message OrderResponse {
  string transaction_id = 1;
  bool success = 2;
  string message = 3;
  int64 timestamp = 4;
}

2.1.2 字段规则与类型速查

规则/类型 说明 常见场景
optional 字段可选,proto3 默认所有字段为 optional 非必填的业务参数
repeated 可变长度数组,可包含零个或多个元素 订单商品列表、事件日志
map<K, V> 键值对映射,key 不能是浮点或 bytes 元数据、标签集合
oneof 一组字段中最多同时设置一个 支付方式选择(信用卡/余额/积分)
enum 枚举类型,必须从 0 开始 订单状态、错误码
事务场景提示: 在分布式事务中,oneof 常用于表示“事务分支类型”(如 TCC 的 Try/Confirm/Cancel),map 用于传递上下文透传字段(如全局事务 ID、分支 ID)。

2.2 一元 RPC(Unary RPC)

一元 RPC 是最基础的请求-响应模式:客户端发送一个请求,服务端返回一个响应。语义上等价于 HTTP/1.1 的 POST 请求,但基于 HTTP/2 和 protobuf 序列化,延迟更低。

2.2.1 服务定义

service OrderService {
  // 一元 RPC:创建订单
  rpc CreateOrder(OrderRequest) returns (OrderResponse);
}

2.2.2 适用场景与事务边界

  • 适用: 简单查询、单记录写入、幂等校验。
  • 事务边界: 一次 RPC 调用对应一个本地数据库事务;若涉及多个资源,需引入分布式事务协调器。
  • 超时处理: 客户端应设置合理超时(如 5s),超时后需考虑是否重试或回滚。
常见陷阱: 一元 RPC 在事务中不能保证“恰好一次”语义。网络抖动可能导致客户端超时但服务端已提交,此时需要幂等设计或补偿机制。

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

客户端发送一个请求,服务端返回一个消息流。适用于服务端需要推送多条数据的场景,如批量查询结果、事件订阅。

2.3.1 服务定义

service OrderService {
  // 服务端流式:查询订单历史
  rpc ListOrders(OrderRequest) returns (stream OrderResponse);
}

2.3.2 事务场景分析

特性 说明 事务影响
流式响应 服务端可发送 0 到 N 条消息 事务提交后,流可能仍在传输,需保证读已提交
背压 客户端通过 Flow Control 控制接收速率 慢消费可能导致服务端缓冲区积压,影响事务锁释放
取消 客户端可随时取消流 取消后服务端应回滚未完成的事务分支
最佳实践: 在 Saga 模式中,服务端流式 RPC 可用于向协调器推送各分支的执行状态,协调器根据流中的事件决定下一步补偿或提交。

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

客户端发送一个消息流,服务端在接收完所有消息后返回一个响应。适用于批量上传、日志聚合、大数据写入等场景。

2.4.1 服务定义

service OrderService {
  // 客户端流式:批量提交订单
  rpc BatchCreateOrders(stream OrderRequest) returns (OrderResponse);
}

2.4.2 事务边界挑战

  • 原子性: 客户端流中的每条消息是否属于同一个事务?通常需要客户端在流结束时显式声明“提交”或“回滚”。
  • 缓冲策略: 服务端应设计缓冲区,在流未结束时暂存数据,避免每条消息都开启独立事务。
  • 错误处理: 流中某条消息校验失败,是否整体回滚?建议使用 oneof 在流末尾携带事务控制指令。
事务模式示例: 客户端流式 RPC 配合 oneof 实现“批量事务”:
message BatchRequest {
  oneof request_type {
    OrderRequest order = 1;
    CommitRequest commit = 2;   // 提交事务
    RollbackRequest rollback = 3; // 回滚事务
  }
}

2.5 双向流式 RPC(Bidirectional Streaming RPC)

客户端和服务端可以独立发送和接收消息流,两者完全异步。这是 gRPC 最灵活的模式,也是分布式事务协调中最常用的模式。

2.5.1 服务定义

service TransactionCoordinator {
  // 双向流式:事务协调
  rpc Coordinate(stream TransactionEvent) returns (stream TransactionResult);
}

2.5.2 四种 RPC 模式对比

模式 请求 响应 典型事务场景 延迟敏感度
一元 RPC 1 1 单资源提交、查询 高
服务端流式 1 N 事件推送、状态广播 中
客户端流式 N 1 批量写入、日志聚合 中
双向流式 N N Saga/TCC 协调、实时协商 低(异步)

2.6 四种 RPC 模式与事务边界关系图

下图展示了四种 RPC 模式在分布式事务中的典型交互拓扑,帮助理解不同模式对事务边界划分的影响。

四种 RPC 模式与事务边界关系 客户端 服务端 ① 一元 RPC 请求 → ← 响应 ② 服务端流式 RPC 请求 → ← 流式响应(多条) ③ 客户端流式 RPC 流式请求(多条)→ ← 响应 ④ 双向流式 RPC 请求流 → ← 响应流 事务边界提示 一元 RPC 边界最清晰;流式 RPC 需要额外设计“事务结束标记”(如 Commit/Rollback 消息)来界定边界

2.7 本章小结

  • Protocol Buffers 是 gRPC 的 IDL 与序列化基础,oneof 和 map 在事务场景中尤为实用。
  • 一元 RPC 适合简单事务,但需处理超时与幂等。
  • 服务端流式 RPC 适用于事件推送,事务边界由流结束信号界定。
  • 客户端流式 RPC 适用于批量事务,需在流末尾显式提交或回滚。
  • 双向流式 RPC 是分布式事务协调的核心模式,支持异步、全双工的事务协商。
下一阶段预告: 在掌握四种 RPC 模式后,我们将进入分布式事务的核心挑战——如何在流式通信中保证 ACID 特性,以及 gRPC 拦截器如何辅助事务上下文的透传。