前言:

微服务拥有自身的数据库, 使得微服务可扩展且易于部署。但是, 天下没有白吃的午餐, 在微服务可扩展且易于部署的背后, 我们将面临著一个相当大的挑战:

当某个事务需要多个微服务才能完成时, 如何保持多个微服务间的数据的一致性? 

本文:

如图一所示, 以客户出售股票订单的这个事务为例:

  • 客户向系统提交了出售股票的订单。
  • 系统生成了订单。
  • 系统预留了客户欲出售股票的股数。
  • 系统向客户收取交易的费用。
  • 系统将客户出售股票的订单提交到股票市场。
图一

对于上述出售股票订单的这个事务, 我们需要下列的微服务来共同的完成:

  • 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

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据