所有分类
  • 所有分类
  • 未分类

分布式事务–消息表+MQ–使用/方案

简介

本文介绍分布式事务的一种解决方案:消息表结合MQ。

本地消息表+MQ(无事务)

简介

说明
项目来源源于eBay经典的BASE方案。ebay的完整方案BASE: An Acid Alternative – ACM Queue
基本设计思想将远程分布式事务拆分成一系列的本地事务
使用的重要技术消息队列和消息应用状态表
优缺点优点:基本避免了分布式事务,实现了“最终一致性”;开发简单
缺点:需设计DB消息表,同时还需要一个后台任务,不断扫描本地消息。导致消息的处理和业务逻辑耦合额外增加业务方的负担。
适用场景适用于对一致性要求不高的非高并发场景。(本地消息队列是BASE理论,是最终一致模型)。

实例流程

说明:这种分布式事务适合前边服务弱依赖于后边的服务的场景,比如付款成功增积分:积分的多少不影响付款。但对于强依赖的情况不合适,如下单减库存操作:是否能下单由库存是否充足而定,而下单是否成功由减库存是否成功而定,此时,用此法最合适:订单模块直接调用减库存的微服务接口。

本处以购物时的付款成功增积分为例说明。

错误处理

  • 步骤:1,2:任意一个出错,整个事务回滚,不会出错
  • 步骤3:投递消息成功,结果接收MQ响应时网络出错。不会出错
  • 步骤4:MQ宕机(解决方法:MQ一般支持持久化)。消费者消费失败(解决方法:重试3次)
  • 步骤4-8:4-8任意一个失败次数超过重试次数。解决方法:发送报警,让人工处理。
            从工程实践角度讲,这种整个流程自动回滚的代价是非常巨大的,不但实现复杂,还会引入新的问题。
    比如自动回滚失败,又怎么处理?对应这种极低概率的case,采取人工处理,会比实现一个高复杂的自动化回滚系统,更加可靠,也更加简单。
  • 步骤6:增积分完成之后,断网,导致增积分成功的消息没有发布出去。解决方法:定时任务会再次将消息投递,步骤5会判断到增积分已完成,直接跳到步骤8:发布消息
  • 一个原子性问题:如果保证消息消费 + insert message到判重表这2个操作的原子性?消费成功,但insert判重表失败,怎么办?关于这个,在Kafka的源码分析系列,第1篇,exactly once问题的时候,有过讨论。

MQ(有事务)

简介

上边“本地消息表+MQ(无事务)”的一个缺点:需要设计DB消息表,同时还需要一个后台任务,不断扫描本地消息。导致消息的处理和业务逻辑耦合额外增加业务方的负担。

为了能解决该问题,同时又不和业务耦合,RocketMQ提出了“事务消息”的概念。阿里巴巴的RocketMQ中实现了分布式事务,实际上其实是对本地消息表的一个封装,将本地消息表移动到了MQ内部

对比方案3和方案2,RocketMQ最大的改变,其实就是把“扫描消息表”这个事情,不让业务方做,而是消息中间件帮着做了。至于消息表,其实还是没有省掉。因为消息中间件要询问发送方,事物是否执行成功,还是需要一个“变相的本地消息表”,记录事物执行状态。

具体来说,就是把消息的发送分成了2个阶段:Prepare阶段和确认阶段。
具体来说,上面的2个步骤,被分解成3个步骤:

  • 第一步:生产者:向RocketMQ发送Prepared消息,生产者会拿到消息的地址。
  • 第二步:生产者:执行本地事务(修改数据库的数据)。
  • 第三步:生产者:
      若第二步执行成功,用第一步拿到的地址去访问消息,并修改状态(Confirm),消息接收者就能使用这个消息。
      若第二步执行失败,用第一步拿到的地址去访问消息,并取消Prepared消息,消息接收者就得不到这个消息。

1. Producer向broker端发送消息
2. 服务端将消息持久化成功之后,向发送方ACK确认消息已经发送成功,此时消息为半消息
3. 发送方开始执行本地事务逻辑
4. 发送方根据本地事务执行结果向服务端提交二次确认(Commit或者Rollback)。服务端收到Commit状态则将半消息标记为可投递,订阅方最终将收到该消息;服务端收到Rollback状态则删除半消息,订阅方将不会接受该消息。
5. 在断网或者是应用重启等特殊情况下,上述步骤4提交的二次确认最终未到达服务端,RocketMQ 会定期扫描消息集群中的事物消息,若发现未经确认的Prepared 消息,会对该消息发起消息回查
6. 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果
7. 发送方根据检查得到的本地事务的最终状态再次提交二次确认,服务端仍按照4对半消息进行操作

错误处理

消费方重试全部失败怎么办?

解决方法:发送报警,让人工处理。

从工程实践角度讲,这种整个流程自动回滚的代价是非常巨大的,不但实现复杂,还会引入新的问题。

比如自动回滚失败,又怎么处理?对应这种极低概率的case,采取人工处理,会比实现一个高复杂的自动化回滚系统,更加可靠,也更加简单。

实例

略       ​

0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录