1. gRPC 基础回顾
在深入事务边界与错误处理之前,必须重新校准 gRPC 的核心知识体系。本章从 核心概念、Protocol Buffers 定义 和 四种通信模式 三个维度展开,为后续中间件落地建立统一的术语与结构基础。
1.1 gRPC 核心概念
gRPC 是一个高性能、开源、通用的 RPC 框架,基于 HTTP/2 协议传输,使用 Protocol Buffers 作为接口定义语言(IDL)。其核心组件包括:
| 概念 | 说明 | 中间件关注点 |
|---|---|---|
| Service | 定义远程方法的逻辑集合 | 拦截器作用范围、事务边界划分 |
| RPC Method | Service 中的具体远程调用方法 | 错误码映射、超时控制粒度 |
| Stub | 客户端代理,屏蔽网络细节 | 重试策略、负载均衡入口 |
| Channel | 与单个 gRPC 服务器的连接抽象 | 连接池管理、TLS 终止 |
| Interceptor | 请求/响应链中的拦截钩子 | 事务传播、错误包装的核心扩展点 |
| Metadata | 键值对形式的附加信息(类似 HTTP Header) | 事务 ID 传递、分布式上下文传播 |
中间件视角: Channel 和 Interceptor 是中间件植入事务边界与错误处理逻辑的两个关键锚点。Channel 负责连接生命周期,Interceptor 负责请求级别的横切逻辑。
1.2 Protocol Buffers 定义
Protocol Buffers(proto3)是 gRPC 的接口定义语言,同时承担数据序列化格式的角色。一个典型的服务定义包含 message 和 service 两个核心元素。
syntax = "proto3";
package ecommerce.order;
// 订单服务 —— 事务边界示例
service OrderService {
rpc CreateOrder (CreateOrderRequest) returns (CreateOrderResponse);
rpc GetOrderStatus (GetOrderStatusRequest) returns (stream OrderStatusUpdate);
rpc UploadInvoice (stream InvoiceChunk) returns (UploadInvoiceResponse);
rpc ProcessPayment (stream PaymentEvent) returns (stream PaymentResult);
}
message CreateOrderRequest {
string user_id = 1;
repeated string product_ids = 2;
double total_amount = 3;
map<string, string> metadata = 4; // 用于传递事务上下文
}
message CreateOrderResponse {
string order_id = 1;
int32 status_code = 2;
string error_message = 3;
}
message OrderStatusUpdate {
string order_id = 1;
string status = 2;
int64 timestamp = 3;
}
message InvoiceChunk {
bytes chunk_data = 1;
int32 chunk_index = 2;
bool is_last = 3;
}
message PaymentEvent {
string transaction_id = 1;
double amount = 2;
string currency = 3;
}
message PaymentResult {
bool success = 1;
string receipt_url = 2;
string error_detail = 3;
}
message UploadInvoiceResponse {
bool accepted = 1;
string invoice_url = 2;
}
关键设计原则:
- 每个 RPC 方法应显式定义独立的 Request/Response 消息,避免复用通用包装器。
- 在 message 中预留
map<string, string> metadata字段,用于中间件注入事务 ID、trace ID 等上下文。 - 错误信息应包含在 response 消息中(如
status_code+error_message),而非仅依赖 gRPC 状态码。
1.3 四种通信模式
gRPC 基于 HTTP/2 的流式能力,定义了四种 RPC 模式。理解每种模式下的数据流特征,是设计事务边界和错误处理策略的前提。
1.4 四种模式的事务边界特征
| 模式 | 请求/响应特征 | 事务边界 | 错误处理时机 | 中间件拦截点 |
|---|---|---|---|---|
| Unary | 1 请求 → 1 响应 | 单次调用内 | 响应到达时 | 客户端/服务端拦截器 |
| Server Streaming | 1 请求 → N 响应 | 从请求到流关闭 | 流中断或结束时 | 服务端拦截器 + 流事件 |
| Client Streaming | N 请求 → 1 响应 | 从首消息到流关闭 | 流关闭后 | 客户端拦截器 + 流事件 |
| Bidirectional | N 请求 ↔ N 响应 | 全双工生命周期 | 任意时刻 | 双向拦截器 + 消息序号 |
常见误区: 很多开发者将 Unary 模式的事务处理逻辑直接套用到流式模式,导致在 Server Streaming 中尝试“回滚已发送的流消息”,或在 Bidirectional 中丢失消息级错误。流式模式必须重新设计事务边界,不能复用 Unary 的简单提交/回滚模型。
1.5 四种模式的代码骨架
以下代码片段展示四种模式在服务端的基本实现骨架,重点标注事务边界和错误处理的占位位置。
// Unary RPC —— 事务边界最清晰
rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse) {
// 事务开始:拦截器可在此注入事务ID
// 业务逻辑:创建订单
// 事务结束:提交或回滚
// 错误处理:返回 gRPC 状态码 + 业务错误码
}
// Server Streaming —— 事务在流开启前决定
rpc GetOrderStatus(GetOrderStatusRequest) returns (stream OrderStatusUpdate) {
// 事务开始:校验请求参数,决定是否开启流
// 流中发送多条 OrderStatusUpdate
// 事务结束:流关闭时提交
// 错误处理:流中断时发送错误状态码
}
// Client Streaming —— 事务在流结束后提交
rpc UploadInvoice(stream InvoiceChunk) returns (UploadInvoiceResponse) {
// 事务开始:接收第一个 chunk 时创建临时缓冲区
// 流中接收多个 InvoiceChunk
// 事务结束:收到 is_last=true 后组装并提交
// 错误处理:流关闭后返回 UploadInvoiceResponse 中的错误字段
}
// Bidirectional Streaming —— 事务需消息级协调
rpc ProcessPayment(stream PaymentEvent) returns (stream PaymentResult) {
// 事务开始:双方协商事务ID
// 流中:每个 PaymentEvent 对应一个 PaymentResult
// 事务结束:任意一方发送结束标记
// 错误处理:消息级错误 + 流级错误双重机制
}
中间件落地要点:
- Unary 模式:拦截器可以安全地在请求前后包裹事务生命周期。
- 流式模式:拦截器需要监听
onOpen、onMessage、onClose事件,不能仅依赖方法前后置。 - 所有模式:Metadata 是传递事务上下文的唯一可靠通道,不要在消息体中硬编码事务 ID。
1.6 本章小结
gRPC 基础回顾为后续章节建立了三个关键认知:
- Protocol Buffers 是服务契约的基石,message 设计直接影响事务上下文的传递能力。
- 四种通信模式 决定了事务边界的形状——Unary 是点,流式模式是线或面。
- Interceptor + Metadata 是中间件实现事务传播和错误包装的标准扩展点,后续章节将在此基础上构建分布式事务协调器。