
前言
微服务拥有自身的数据库, 使得微服务可扩展且易于部署。但是, 天下没有白吃的午餐, 在微服务可扩展且易于部署的背后, 我们将面临著一个相当大的挑战:
当某个事务需要多个微服务才能完成时, 如何保持多个微服务间的数据的一致性?
Saga事务
如图一所示, 以客户出售股票订单的这个事务为例:
- 客户向系统提交了出售股票的订单。
- 系统生成了订单。
- 系统预留了客户欲出售股票的股数。
- 系统向客户收取交易的费用。
- 系统将客户出售股票的订单提交到股票市场。

对于上述出售股票订单的这个事务, 我们需要下列的微服务来共同的完成:
- Orders: 接收客户出售股票订单的请求、生成订单。
- Account Transactions: 预留了客户欲出售股票的股数。
- Fee: 向客户收取交易的费用。
- Market: 将客户出售股票的订单提交到股票市场。
如图二所示, 当微服务 Orders 接收客户出售股票订单的请求、校验订单, 确认订单无误后, 便会产生订单, 并且发出订单已产生的事件; Order_Created。
在事件驱动微服务 (Event-Driven Microservices) 的架构下, 微服务 Account Transactions、Fee、Market 都订阅 (Subscribe) 了事件 Order_Created。
也就是说当事件 Order_Created 产生后, 将驱动微服务 Account Transactions、Fee、Market 都可各自独立的运作; 微服务 Account Transaction、Fee、Market 之间不存在著执行先后顺序的问题。

也就是说, 在微服务 Market 还没完成出售股票前, 微服务 Account Transaction 可先将客户帐户股票的股数先扣除、微服务 Fee 可先向客户收取交易的费用。
而当微服务 Market 最终还是没完成出售股票, 微服务 Account Transaction、微服务 Fee 便必需要回滚 (Roll Back)。
在事件驱动微服务的架构下, 我们是否可运用 2PC (Two-Phase Commit) 的机制, 使得微服务 Account Transaction、微服务Fee 可高效的回滚?
答案是否定的!
为何?
让我们先一起来回顾下什么是 2PC (Two-Phase Commit) ?
2PC 运用所谓的事务管理者 (transaction manager), 将事务的操作分配到多个的资源 (Resources); 在 prepare 阶段, 事务管理者指引著资源, 将所需的操作准备就绪。在 commit 阶段, 事务管理者指引著资源 commit 或 abort 先前所准备就绪的操作。
2PC 应用在分布式的事件驱动微服务, 将显得缺乏效率, 因为:
- 2PC 使用了同步通信的方式在事务管理者与资源之间。同步通信的方式将使得事务管理者需耗费大量的等待时间, 才能确认所有的资源都已成功的commit, 或是因某个资源的失败, 而使得其他的资源都需进行回滚。
- 事务操作涉及到的多个资源, 都将会被锁 (lock)。所以, 当某个资源的操作将耗费长时间时, 将会使得其它的资源发生死锁 (deadlock) 或争夺(contention)。
所以, 在分布式的事件驱动微服务下, 2PC 是相当的不适用的。
再回到前述的客户出售股票订单的例子:
- 当事件 Order_Created 产生后, 2PC 使得微服务 Account Transaction、Fee、Market 之间存在著执行先后顺序的问题。
- 2PC 无法使得微服务 Account Transaction、Fee、Market 都可各自独立的运作。
2PC 无法使得微服务 Account Transaction、Fee、Market 可高效的维持数据的一致性。
2PC 不适用于分布式的事件驱动微服务。
那使得微服务 Account Transaction、Fee、Market 可高效的维持数据的一致性的方法到底是什么?
答案是: Orchestrated Sagas。
Orchestrated Sagas; 前一个的步骤触发下一个的步骤; 可使得在某个的事务内的多个的微服务, 经由事件驱动, 使得各微服务间可以经由异步的方式互相的触发, 而使得各微服务间可高效的达到数据的一致性。
以另一个线上购物的例子为例:
在某个线上购物的网站, 我们需要下列的微服务来共同的完成客户的采购:
- OrderManagement: 接收客户订单的请求、生成订单。
- Invoice: 处理客户付款。
- Shipment: 处理出货。
我们设计了 OrderManagementSaga; 使得微服务 OrderManagement, Invoice, Shipment 可在客户采购的事务内维持数据的一致性。
OrderManagementSaga 内的步骤如下:
- 微服务 OrderManagement, 接收客户订单的请求、生成订单, 产生相对应Invoice, Shipment, 并且发出事件; OrderCreatedEvent。
- 微服务 Invoice, Shipment 各自独立的运作; 微服务 Invoice 处理客户付款, 并且发出事件; InvoicePaidEvent。微服务 Shipment 处理出货, 并且发出事件; ShippingArrivedEvent。
- 确认客户已收到货并且已完成付款后, 此客户采购的事务便完成, OrderManagementSaga 便也结束。
我们以 Axon 框架来实现了 OrderManagementSaga; 样例代码如下:
public class OrderManagementSaga {
private boolean paid = false;
private boolean delivered = false;
@Inject
private transient CommandGateway commandGateway;
@StartSaga
@SagaEventHandler(associationProperty = "orderId")
public void handle(OrderCreatedEvent event) {
// client generated identifiers
ShippingId shipmentId = createShipmentId();
InvoiceId invoiceId = createInvoiceId();
// associate the Saga with these values, before sending the commands
SagaLifecycle.associateWith("shipmentId", shipmentId);
SagaLifecycle.associateWith("invoiceId", invoiceId);
// send the commands
commandGateway.send(new PrepareShippingCommand(...));
commandGateway.send(new CreateInvoiceCommand(...));
}
@SagaEventHandler(associationProperty = "shipmentId")
public void handle(ShippingArrivedEvent event) {
delivered = true;
if (paid) { SagaLifecycle.end(); }
}
@SagaEventHandler(associationProperty = "invoiceId")
public void handle(InvoicePaidEvent event) {
paid = true;
if (delivered) { SagaLifecycle.end(); }
}
// ...
}
Axon 框架有著以下的特点:
我们只需专注在 Sagas 的宣告与业务逻辑; AXON会帮我们完成:
- Sagas 的生成; @StartSaga 代表 OrderManagementSaga 的生成、启动。
- Sagas 的持久化(存储)
- Sagas 间关联的建立

所以, Axon 框架大大的提升了事件驱动微服务开发上的效率与质量。
结论
Orchestrated Sagas 的优点是显而易见的:
- 使得事务内的各微服务可完全独立、自主的运作; 微服务间完全是松耦合的。
Orchestrated Sagas 当然不是完美的; Orchestrated Sagas 增加了开发的复杂度。
- 以先前的线上购物网站为例; 微服务 OrderManagement 要能追踪客户订单的状态是生成, 取消, 拒绝签收或完成, 在开发上将是有点复杂度的事。
虽然, Orchestrated Sagas 增加了开发的复杂度, 但是却可使得微服务间可完全的松耦合, 使得我们可更高效的提升对客户、对市场响应的速度。
欢迎你也来试试!
参考资料:
Microservices In Action; Morgan Bruce, Paulo A. Pereira
Axon Framework; http://www.axonframework.org