3. gRPC 分布式事务模型:Saga 与 TCC 模式实现及上下文传播

在微服务架构中,跨多个 gRPC 服务的分布式事务是压测场景中最常见的故障触发点。本章深入剖析两种主流补偿型事务模式——Saga 与 TCC 在 gRPC 中的实现原理,并重点讲解如何通过 gRPC 拦截器实现事务上下文的透明传播。

3.1 Saga 模式在 gRPC 中的实现

Saga 模式将一个分布式事务拆分为一组本地事务(Local Transaction),每个本地事务对应一个 gRPC 服务调用,并配备一个补偿操作(Compensation)。当某个步骤失败时,Saga 协调器按逆序调用补偿操作,回滚已提交的步骤。

核心要点:Saga 不要求资源锁定,适用于长事务、高并发场景;但补偿逻辑必须幂等,且需处理部分失败与悬挂补偿。

3.1.1 实现架构

在 gRPC 生态中,Saga 通常通过一个独立的 Saga Orchestrator 服务实现。该服务使用 gRPC 客户端调用各参与方的业务接口与补偿接口。

Saga Orchestrator 在 gRPC 中的调用流程 Saga Orchestrator Step 1: 创建订单 Step 2: 扣减库存 Step 3: 扣减余额 补偿: 恢复余额 补偿: 恢复库存 补偿: 取消订单 调用 调用 调用 Step 3 失败

3.1.2 gRPC 接口定义示例

// saga.proto
service OrderService {
  rpc CreateOrder (CreateOrderRequest) returns (CreateOrderResponse);
  rpc CompensateCreateOrder (CompensateCreateOrderRequest) returns (CompensateResponse);
}

service InventoryService {
  rpc DeductStock (DeductStockRequest) returns (DeductStockResponse);
  rpc CompensateDeductStock (CompensateDeductStockRequest) returns (CompensateResponse);
}

service AccountService {
  rpc DeductBalance (DeductBalanceRequest) returns (DeductBalanceResponse);
  rpc CompensateDeductBalance (CompensateDeductBalanceRequest) returns (CompensateResponse);
}
压测关注点:Saga 模式下,补偿接口的响应时间与成功率直接影响事务最终一致性。压测时需模拟“部分步骤成功 + 后续步骤失败”的场景,验证补偿链路是否完整执行。

3.2 TCC 模式在 gRPC 中的实现

TCC(Try-Confirm-Cancel)模式将每个分布式事务操作显式分为三个阶段:Try(预留资源)、Confirm(确认执行)、Cancel(取消释放)。相比 Saga,TCC 在 Try 阶段即锁定资源,能提供更强的隔离性。

3.2.1 三阶段调用模型

阶段 gRPC 方法 业务语义 失败处理
Try TryDeductBalance 冻结用户余额,标记为不可用 直接返回失败,不执行后续阶段
Confirm ConfirmDeductBalance 将冻结余额正式扣除 幂等重试,直到成功
Cancel CancelDeductBalance 解冻余额,恢复可用额度 幂等重试,直到成功

3.2.2 TCC 事务管理器与 gRPC 集成

TCC 通常需要一个 Transaction Manager (TM) 来协调全局事务。TM 通过 gRPC 调用各参与方的 Try 接口,根据 Try 结果决定调用 Confirm 或 Cancel。

TCC 三阶段在 gRPC 中的调用时序 TM Service A Service B Service C Try() Try() Try() 所有 Try 成功? 是 Confirm() Confirm() Confirm() 否 Cancel() 所有
压测陷阱:TCC 的 Try 阶段如果超时或返回失败,TM 必须立即调用所有已成功 Try 的服务的 Cancel 接口。压测时需构造“部分 Try 成功 + 部分 Try 超时”的混合场景,验证 Cancel 是否被正确触发且幂等。

3.3 gRPC 拦截器与事务上下文传播

无论是 Saga 还是 TCC,分布式事务都需要将全局事务 ID(XID)、分支 ID、事务模式等上下文信息透明地传递到每个 gRPC 调用中。gRPC 拦截器(Interceptor)是实现这一机制的最佳切入点。

3.3.1 上下文传播架构

事务上下文通过 gRPC 的 Metadata 在客户端与服务端之间传递。客户端拦截器在发起调用前将上下文注入 Metadata;服务端拦截器在接收请求时从 Metadata 中提取上下文,并绑定到当前线程或协程。

组件 职责 gRPC 拦截器类型
客户端拦截器 从当前线程上下文读取 XID,写入 gRPC Metadata ClientInterceptor
服务端拦截器 从 Metadata 读取 XID,设置到当前线程上下文 ServerInterceptor
传播键 Metadata 中使用的 Key,如 "xid" 常量字符串

3.3.2 客户端拦截器实现示例(Java)

public class TransactionClientInterceptor implements ClientInterceptor {
    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
            MethodDescriptor<ReqT, RespT> method,
            CallOptions callOptions,
            Channel next) {

        return new ForwardingClientCall.SimpleForwardingClientCall<>(next.newCall(method, callOptions)) {
            @Override
            public void start(Listener<RespT> responseListener, Metadata headers) {
                // 从当前线程上下文获取全局事务 ID
                String xid = TransactionContextHolder.getXID();
                if (xid != null) {
                    headers.put(Metadata.Key.of("xid", Metadata.ASCII_STRING_MARSHALLER), xid);
                }
                super.start(responseListener, headers);
            }
        };
    }
}

3.3.3 服务端拦截器实现示例(Java)

public class TransactionServerInterceptor implements ServerInterceptor {
    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
            ServerCall<ReqT, RespT> call,
            Metadata headers,
            ServerCallHandler<ReqT, RespT> next) {

        // 从 Metadata 中提取全局事务 ID
        String xid = headers.get(Metadata.Key.of("xid", Metadata.ASCII_STRING_MARSHALLER));
        if (xid != null) {
            TransactionContextHolder.setXID(xid);
        }

        return Contexts.interceptCall(Context.current(), call, headers, next);
    }
}
压测验证方法:在压测脚本中,手动构造携带错误 XID 或缺失 XID 的 Metadata,验证服务端拦截器是否能正确拒绝或降级处理。同时,通过分布式链路追踪工具(如 Jaeger)检查 XID 是否在跨服务调用中完整传递。

3.4 三种实现方式对比

维度 Saga TCC 拦截器上下文传播
资源锁定 不锁定,最终一致性 Try 阶段锁定 不涉及
隔离性 弱(无隔离) 强(资源预留) 不涉及
实现复杂度 中(需补偿逻辑) 高(三阶段 + 幂等) 低(拦截器注入)
压测重点 补偿链路完整性 Try/Cancel 幂等性 上下文透传正确性
适用场景 长事务、高吞吐 短事务、强一致性 所有分布式事务
故障排查要点:在压测场景库中,Saga 模式最常见的故障是补偿操作未执行或重复执行;TCC 模式最常见的故障是 Cancel 阶段因网络问题未到达,导致资源永久锁定;拦截器上下文传播最常见的故障是 XID 丢失,导致事务管理器无法关联全局事务。这三种故障都应作为压测场景库的必测用例。