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