【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)

时间:2022-11-01 11:16:16

基于MQ的分布式事务(MQ事务最终一致性)

方案简介

基于MQ 的分布式事务方案其实是对本地消息表的封装,将本地消息表基于MQ 内部,其他方面的协议基本与本地消息表一致。

【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)

本地消息表:最终一致性

方案简介

本地消息表的方案最初是由eBay提出,核心思路是将分布式事务拆分成本地事务进行处理。

  • 通过在事务主动发起方新建事务消息表事务发起方处理业务记录事务消息在本地事务中完成,轮询事务消息表的数据发送事务消息,事务被动方基于消息中间件消费事务消息表中的事务。
  • 这样设计可以避免”业务处理成功 + 事务消息发送失败",或"业务处理失败 + 事务消息发送成功"的棘手情况出现,保证 2 个系统事务的数据一致性。
处理流程
  • 把分布式事务最先开始处理的事务方称为事务主动方
  • 在事务主动方之后处理的业务内的其他事务称为事务被动方。

下面继续以电商下单为例进行方案解析,这里把整个过程简单分为扣减库存,订单创建 2 个步骤。

  1. 库存服务和订单服务分别在不同的服务器节点上,其中库存服务是事务主动方,订单服务是事务被动方
  2. 事务的主动方需要新建事务消息表,用于记录分布式事务的消息的发生、处理状态。
事务发起

【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)

事务回调

【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)

事务分布流程
步骤1-事务主动方处理本地事务。
  • 事务主动方在本地事务中处理业务更新操作和写消息表操作。

例:库存服务阶段在本地事务中完成扣减库存和写消息表

步骤 2-事务主动方通过消息中间件,通知事务被动方处理事务通知事务待消息。
  • 消息中间件可以基于 Kafka、RabbitMQ 消息队列,事务主动方主动写消息到消息队列,事务消费方消费并处理消息队列中的消息。

例:库存服务把事务待处理消息写到消息中间件,订单服务消费消息中间件的消息,完成新增订单

步骤 3-事务被动方通过消息中间件,通知事务主动方事务已处理的消息。
  • 为了数据的一致性,当处理错误需要重试,事务发送方和事务接收方相关业务处理需要支持幂等。

例,订单服务把事务已处理消息写到消息中间件,库存服务消费中间件的消息,并将事务消息的状态更新为已完成

保存一致性的容错处理
步骤 1 处理出错

事务回滚,相当于什么都没发生。

步骤2+步骤3处理出错

由于未处理的事务消息还是保存在事务发送方,事务发送方可以定时轮询为超时消息数据,再次发送到消息中间件进行处理。事务被动方消费事务消息重试处理

  • 如果业务上的失败事务被动方可以发消息给事务主动方进行回滚
  • 如果多个事务被动方已经消费消息事务主动方需要回滚事务时需要通知事务被动方回滚
方案总结
方案的优点
  • 从应用设计开发的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于消息中间件,弱化了对 MQ 中间件特性的依赖。
  • 方案轻量,容易实现。
方案的缺点
  1. 与具体的业务场景绑定,耦合性强,不可公用
  2. 消息数据与业务数据同库,占用业务系统资源
  3. 业务系统在使用关系型数据库的情况下,消息服务性能会受到关系型数据库并发性能的局限

基于RocketMQ的分布式事务(数据的最终一致性)

基于RocketMQ4.3之后的版本介绍 MQ 的分布式事务方案。

在本地消息表方案中,保证事务主动方发写业务表数据和写消息表数据的一致性是基于数据库事务,RocketMQ的事务消息相对于普通 MQ,相对于提供了 2PC 的提交接口,方案如下

【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)

正常情况:事务主动方发消息

【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)

事务主动方服务正常,没有发生故障,发消息流程如下:

  1. 发送方向 MQ 服务端(MQ Server)发送 half 消息。
  2. MQ Server 将消息持久化成功之后,向发送方 ack 确认消息已经发送成功。
  3. 发送方开始执行本地事务逻辑。
  4. 发送方根据本地事务执行结果向MQ Server 提交二次确认(commit 或是 rollback)。
  5. MQ Server 收到 commit 状态则将半消息标记为可投递订阅方最终将收到该消息MQ Server 收到 rollback 状态则删除半消息,订阅方将不会接受该消息
异常情况:事务主动方消息恢复

【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)

在断网或者应用重启等异常情况下,第四步提交的二次确认超时未到达 MQ Server,此时处理逻辑如下:

  1. MQ Server 对该消息发起消息回查。
  2. 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
  3. 发送方根据检查得到的本地事务的最终状态再次提交二次确认。
  4. MQ Server基于 commit/rollback 对消息进行投递或者删除。

介绍完 RocketMQ 的事务消息方案后,由于前面已经介绍过本地消息表方案,这里就简单介绍 RocketMQ 分布式事务:

【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)

【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)

  • 事务主动方基于MQ通信通知事务被动方处理事务,事务被动方基于 MQ 返回处理结果。
  • 事务被动方消费消息异常,需要不断重试,业务处理逻辑需要保证幂等。
  • 事务被动方业务上的处理失败,可以通过 MQ 通知事务主动方进行补偿或者事务回滚。

方案总结

相比本地消息表方案,MQ 事务方案优点是:
  1. 消息数据独立存储 ,降低业务系统与消息系统之间的耦合。
  2. 吞吐量优于使用本地消息表方案。
缺点
  • **一次消息发送需要两次网络请求(half 消息 + commit/rollback 消息) **。
  • 业务处理服务需要实现消息状态回查接口。