微服务设计模式 - 04 使用 Saga 管理事务

导读

  • 为什么分布式事务不适合现代应用程序
  • 使用 Saga 模式维护微服务架构的一致性
  • 使用协同和编排这两类方式协调 Saga
  • 采用对策来解决缺乏隔离的问题

Spring 使用 @Transactional 注解来让方法调用自动的在事务范围内完成, 在只访问一个数据库的单体应用中, 事务管理简单明了.

微服务架构下, 服务往往都有自己的私有数据库, 在这种情况下, 应用程序必须使用一些高级的事务管理机制来管理事务.

 

你需要一种机制来保证多数据库环境下的数据一致性.

分布式并不简单, 它的本质是进程间通信,这会降低分布式系统的可用性, 为了让一个分布式事务提交完成,所有参与事务的服务都必须可用.

分布式理论 CAP 早已证明, 系统只能在它的一致性,可用性和分区容错性 三个属性中保证两个, 今天架构师更强调可用性而放弃数据强一致性.

所以, 实际上, 分布式事务并不是一个好的选择,为了解决这个问题, 需要构建 松耦合,异步服务之上的 Saga.

Saga 模式: 通过使用异步消息来协调一系列本地事务,从而维护多个服务之间的数据一致性.

 

这个Saga 包含了以下几个本地事务:

1. Order Service: 创建一个处于 approval_pending 状态 order.

2. Consumer Service: 验证当前订单中的消费者可以下单.

3. Kitchen Service: 验证订单内容,并创建后厨 ticket, 状态为 create_pending.

4. accounting service: 对消费者提供的信用卡做授权操作.

5. Kitchen Service: 把后厨工单 ticket 状态改为 awaiting_acceptance.

6. Order Service: 把 Order 状态改为 approved.

当本地事务完成时, 服务会发布消息. 然后,此消息将触发 Saga 中的下一个步骤, 使用消息不仅可以确保 Saga 参与方之间的松散耦合, 还可以保证 Saga 完成. 这是因为如果消息的接收方暂时不可用, 则消息代理会缓存消息, 直到消息被投递为止. 目前我们碰到两个问题:

Q1. Saga 之间缺乏隔离.

缺乏隔离可能会导致 3 个问题:

 

 Saga 事务包含 3 类:

 

 

 

 

语义锁: Saga 的可补偿性事务会在其创建或更新的任何记录中设置标志. 该标志表示该记录未提交且可能更改. 该标志可以是阻止其他事务访问记录的锁. 

Q2. 发生错误时的回滚更改.

  Saga 按照正常事务的反向顺序来执行补偿事务(回滚)

那么 Saga 是如何做到的呢?

  • 协同式: 把 Saga 的决策和执行顺序逻辑分布在 Saga 的每一个参与方中, 它们通过交换事件的方式来进行沟通.
  • 编排式: 把 Saga 的决策和执行顺序逻辑集中在一个 Saga 编排器类中, Saga 编排器发出命令式消息给各个 Saga 参与方, 指示这些参与方服务完成具体操作.(本地服务)

协同式

 

好处:

简单: 服务在创建, 更新和删除业务对象时发布事件.

松耦合: 参与方订阅事件并且彼此之间不会因此产生耦合. (失败事件也要发布)

弊端:

更难理解: 没有定义 Saga, 不知道哪些事务是 combine 在一个 Saga 中的.

服务之间的循环依赖关系: Saga 参与方订阅彼此的事件, 这通常会导致循环依赖(这通常不是一个好的设计风格)

编排式 Saga (通常更推荐) 

当使用 Saga 编排式时, 开发人员定义一个编排器类, 这个类的唯一职责就是告诉 Saga 的参与方该做什么事情. Saga 编排器采用命令/异步响应方式与Saga参与方通信, 为了完成 Saga 中的一个环节, 编排器对某个参与方发出一个命令式的消息, 告诉这个参与方该做什么操作, 当参与方服务完成操作后, 会给编排器发送一个答复消息, 编排器处理这个消息, 并决定 Saga 的下一步操作.

 

把 Saga 编排器视为一个状态机.

状态机由一组状态和一组事件触发的状态之间的转换组成. 每个转换都可以有一个动作, 对 Saga 来说动作就是对某个参与方的调用, 状态之间的转换由 Saga 参与方执行的本地事务完成触发, 当前状态和本地事务的特定结果决定了状态转换以及执行的动作. 对状态机也有有效的测试策略, 因此,使用状态机模型可以更轻松哦的设计,实现,测试 Saga.

 

 

posted @ 2021-06-24 12:01  神之一招  阅读(335)  评论(0编辑  收藏  举报