1. 分布式事务
在前面文章《分布式事务》中介绍了几种分布式事务,其中Saga介绍了相关的概念,接下来介绍Saga使用案例,案例来源《微服务架构设计模式》。
2. 案例需求分析
2.1 一个成功的订单创建流程
实现餐馆系统中的创建订单createOrder()操作。这个操作必须验证消费者是否满足下订单的相关条件、验证订单内容、完成消费者的信用卡授权,以及在数据库中创建Order。一个成功的订单创建流程:
- 创建一个待处理订单;
- 验证订单消费者可以下单;
- 创建后厨工单;
- 对消费者提供的信用卡进行授权操作;
- 更新后厨工单状态为“接受”状态;
- 更新订单状态为“通过”。
在单体应用中,这样的操作是相对直观和容易实现的。在验证中需要的所有数据都可以从数据库中直接读取,此外,可以使用一个ACID类事务来保证数据的一致性。
在微服务架构下实现同样的操作则颇有难度。如图一所示,所需要的验证数据散布在不同的服务中。createOrder()操作必须访问多个服务(包括:ConsumerService,KitchenService和AccountingService)来获得它所需要的验证数据。
createOrder()操作涉及三个服务:
- 订单服务:Order Service。功能:创建订单。
- 消费者服务:Consumer Service。功能:验证当前订单中的消费者是否可下单。
- 后厨服务:Kitchen Server。功能:验证订单内容,创建后厨工单Ticket。
- 账户服务:Account Service。功能:对消费者提供的信用卡授权。
订单创建时序图:
一个订单创建操作依赖三个服务原因是,实例中对餐饮服务进行了微服务话拆分,服务拆分使用了“领域驱动设计模式”,详情见《微服务架构设计模式》。备注:“领域驱动设计模”适合在复杂的业务模型使用。
2.2 存在的挑战
订单创建操作中涉及的三个服务,每个服务都有自己的私有数据库,怎样保障多数据库环境下的数据一致性?我们选择使用Saga模式来维护数据一致性。
3. 使用Saga模式维护数据一致性
Saga是一种在微服务架构中维护数据一致性的机制,它可以避免分布式事务所带来的问题。
- 一系列操作:一个Saga表示需要更新多个服务中数据的一个系统操作。
- 一连串本地事务:Saga由一连串的本地事务组成。每一个本地事务负责更新它所在服务的私有数据库,这些操作依赖于ACID 事务框架和函数库。
- 使用补偿事务:由于Saga缺少ACID事务的隔离性,因此必须使用补偿事务回滚Saga。
- 依次触发执行:系统操作启动了Saga的第一步。完成本地事务会触发下一个本地事务的执行。
4. 使用Saga实现订单创建实例
使用Saga实现前文中案例createOrder()操作。Saga事务操作流程如下图:
这个Saga包含了以下几个本地事务:
- Order Service:创建一个处于APPROVAL_PENDING 状态的 Order。
- Consumer Service:验证当前订单中的消费者可以下单。
- Kitchen Service:验证订单内容,并创建一个后厨工单Ticket,状态为CREATE PENDING
- Accounting Service:对消费者提供的信用卡做授权操作。
- Kitchen Service:把后厨工单Ticket 的状态改为AWAITING_ACCEPTANCE。
- Order Service:把Order 的状态改为 APPROVED。
关键点:
- Saga事务实现流程:当本地事务完成时,服务发布消息;此消息将触发Saga中的下一个步骤。
- 使用消息保证Saga完成:使用消息不仅可以确保Saga参与方之间的松散耦合,还可以保证Saga完成。因为如果消息的接收方暂时不可用,则消息代理会缓存消息,直到消息可以被投递为止。
- Saga挑战:一个挑战是Saga之间缺乏隔离。另一个挑战是在发生错误时的回滚更改。