引言
在微服务架构中,每个服务拥有独立的数据库,跨服务的业务操作涉及多个数据库,传统的ACID事务(如2PC两阶段提交)因强一致性和阻塞问题,几乎不再适用。Saga模式是微服务中解决分布式事务的主流方案,它通过本地事务串接与补偿操作,在保证最终一致性的同时维持系统的高可用性。
本文将深入剖析Saga的两种实现模式——编排模式(Choreography)和协调模式(Orchestration),给出完整的代码示例,并分享生产环境中的设计要点与陷阱规避。
什么是Saga?
Saga最初由Hector Garcia-Molina和Kenneth Salem在1987年的论文《Sagas》中提出。一个Saga是一系列本地事务(T1, T2, …, Tn)的序列,每个本地事务都会更新数据库并发布事件或消息触发下一个事务。如果某个本地事务(Ti)失败,Saga将执行补偿事务(Ci, Ci-1, …, C1)来回滚之前的所有正面操作。
核心特点:
- 每个Ti都是可独立提交的本地ACID事务。
- Ci是Ti的补偿操作,必须是幂等的、可逆的。
- 不保证隔离性(中间状态对外可见),需要业务层处理。
编排模式(Choreography-based Saga)
在编排模式中,没有中央协调器。每个服务监听上游事件,完成自己的本地事务后发布下游事件,像一支无声的舞蹈,每个舞者听音乐自行起舞。
流程示例:电商下单
- 订单服务创建订单(状态=待处理)→ 发布
OrderCreated事件。 - 库存服务监听
OrderCreated,预扣库存 → 发布StockReserved事件。 - 支付服务监听
StockReserved,发起扣款 → 发布PaymentCompleted事件。 - 订单服务监听
PaymentCompleted,将订单状态改为"已确认"。
失败补偿:如果支付失败,支付服务发布 PaymentFailed 事件,库存服务监听后释放库存,订单服务监听后取消订单。
编排模式代码示例
// 订单服务 — 创建订单并发布事件
@Service
public class OrderService {
@Transactional
public void createOrder(OrderRequest req) {
Order order = orderRepository.save(new Order(req, OrderStatus.PENDING));
// 发布事件到消息队列
eventPublisher.publish(new OrderCreatedEvent(order.getId(), req.getItems()));
}
}
// 库存服务 — 监听订单事件并预扣库存
@Component
public class InventoryService {
@EventListener
@Transactional
public void onOrderCreated(OrderCreatedEvent event) {
try {
inventoryRepository.reserveStock(event.getItems());
eventPublisher.publish(new StockReservedEvent(event.getOrderId()));
} catch (InsufficientStockException e) {
eventPublisher.publish(new StockReservationFailedEvent(event.getOrderId()));
}
}
// 补偿操作:释放库存
@EventListener
@Transactional
public void onPaymentFailed(PaymentFailedEvent event) {
inventoryRepository.releaseStock(event.getOrderId());
}
}
优缺点
- 优点:去中心化、无单点故障,适合简单流程;开发量小,新增参与者只需添加事件监听。
- 缺点:流程逻辑分散在多个服务中,难以追踪整体走向;随着步骤增多,事件依赖网会变得复杂;测试时需要同时启动多个服务。
协调模式(Orchestration-based Saga)
在协调模式中,引入一个中央协调器(Saga Orchestrator),该协调器负责按顺序调用各服务,并决定何时执行补偿操作。协调器如同指挥家,每个乐手只对指挥家负责。
流程示例:同一电商下单
- Saga协调器调用 订单服务:创建订单。
- 协调器调用 库存服务:预扣库存。
- 协调器调用 支付服务:执行扣款。
- 所有步骤成功 → Saga完成。
- 若步骤3失败 → 协调器调用 库存服务(释放库存)→ 调用 订单服务(取消订单)。
协调模式代码示例(基于 Camunda / 开源轴框)
// Saga 协调器 — 流程定义
public class CreateOrderSaga {
private final OrderServiceProxy orderService;
private final InventoryServiceProxy inventoryService;
private final PaymentServiceProxy paymentService;
public void execute(OrderRequest request) {
SagaDefinition saga = SagaBuilder.saga()
.step()
.invoke(orderService::createOrder)
.withCompensation(orderService::cancelOrder)
.step()
.invoke(inventoryService::reserveStock)
.withCompensation(inventoryService::releaseStock)
.step()
.invoke(paymentService::processPayment)
.withCompensation(paymentService::refund)
.build();
saga.execute(request);
}
}
// 使用开源框架的注解方式(伪代码)
@Compensable(compensationMethod = "cancelOrder")
public OrderResult createOrder(OrderRequest req) {
return orderRepository.save(new Order(req));
}
public void cancelOrder(OrderRequest req) {
orderRepository.deleteByRequestId(req.getRequestId());
}
优缺点
- 优点:流程逻辑集中管理,便于监控和调试;协调器可在失败时使用不同策略。
- 缺点:协调器是单点(需要高可用实现);协调器需了解所有服务的API,出现了逻辑集中化的风险。
两种模式的深度对比
| 维度 | 编排模式 | 协调模式 |
|---|---|---|
| 控制流 | 去中心化,事件驱动 | 中心化,命令式驱动 |
| 逻辑位置 | 分散于各服务 | 集中于Saga协调器 |
| 新增步骤 | 添加事件监听即可 | 修改协调器流程定义 |
| 故障排查 | 难:分布式追踪必需 | 易:集中状态监控 |
| 事务状态 | 隐式(无全局状态) | 显式(协调器维护状态) |
| 循环依赖风险 | 可能出现事件死循环 | 协调器统一编排,无此风险 |
| 性能开销 | 低(事件异步传递) | 高(协调器等待同步调用) |
| 适用场景 | ≦5步、简单流程、独立团队 | >5步、复杂流程、集中管理 |
生产环境关键设计要点
1. 幂等性设计
Saga在重试或补偿触发时可能多次执行同一操作,所有操作必须幂等。例如:
-- 使用唯一业务键防止重复扣款
UPDATE balance SET amount = amount - :amt
WHERE user_id = :uid AND request_id = :reqId AND status = 'PENDING';
2. 补偿操作的"语义可逆"
并非所有操作都能完美补偿。例如"发送邮件"不可逆,此时应采用确务设计(Idempotent Receiver),在发送前记录意图,实际发送时去重。
- 可逆补偿:库存释放(数学上可逆)
- 不可逆操作:短信通知 → 采用"撤销窗口"或业务对账补偿
3. 状态记录与可观测性
建议维护 saga_instance 表记录每个Saga的当前状态、已完成的步骤、补偿进度,并按照OpenTelemetry标准记录Trace ID。
4. 长事务与用户体感
Saga的每个本地事务是即时提交的,但整个Saga的完成可能需要数秒甚至数分钟。建议:
- 使用"预创建"模式:订单先创建为"处理中",Saga完成后确认
- 前端通过WebSocket或轮询获取最终状态
- 超时告警:超过阈值自动触发人工干预
5. 选择合适的消息中间件
推荐使用支持"事务消息"或"消息表方案"的中间件,确保事件一定送达。例如RocketMQ的事务消息、Kafka的幂等生产者。
开源框架推荐
- Seata:阿里巴巴开源,支持Saga、TCC、AT等多种模式,Spring友好。
- Camunda:基于BPMN2.0的轻量级工作流引擎,可用JSON定义Saga流程。
- Eventuate Tram Sagas:Gregory Young专为微场景设计的Java库,支持编排与协调双模式。
- Axon Framework:CQRS+Event Sourcing框架,内置Saga支持。
总结
Saga不是一种具体的框架,而是一种架构模式。选择编排模式还是协调模式,取决于团队的微服务成熟度、流程的复杂度以及对集中管控的接受程度。记住:
- 编排模式 = "去中心化通信",适合小而美的服务网格。
- 协调模式 = "集中式协调",适合流程严谨的企业级业务。
在实际项目中,混合使用两种模式也不少见——同一系统内简单流程用编排,复杂流程用协调。关键是坚持最终一致性的心智模型,把每一次Saga都当作一次可能中途折返的长途旅行来设计。

发表评论 取消回复