RocketMQ事务消息篇(一)之事务消息的介绍

事务

提到“事务”一词,一般首先会想到数据库事务,也是日常开发中大家接触比较多的。

说到数据库事务就会想到它的4个特性(ACID):原子性、一致性、隔离性和持久性。它的所有特性最终也是为了保证它的一致性--数据一致性:即事务执行前和执行后,数据状态都是正确的,这个“正确”指的是符合我们预期的。比如,A要转B100元,最终结果应当是A少了100元且A的余额大于等于0,B应当增加100元或者A没转成功,都没减少也没增加。而不应该出现其它情况,如A的钱减少了,B却没有增加或者A的余额小于0了等。

MQ的事务也是为了保证数据的最终一致性。

MQ的分布式事务

分布式事务就是在分布式系统来实现的事务,消息中的事务之所以说它是分布式事务,是因为事务的参与者及事务管理器:消息发送端(producer),消息服务器(broker),消息的消费端(consumer)位于分布式系统的不同节点上。

p.s.   当然,你的发送端和消费端也可能在一个应用中,即在一个节点上。

MQ的事务消息就是为了解决消息发送端、发送端的本地事务及消费端的一致性,在RoketMQ中消费端不用考虑,因为至少会消费一次。所以它主要就是解决发送端的消息发送和它的本地事务逻辑的一致性。

RocketMQ的事务消息

RocketMQ的事务消息就是分布式事务的一种解决方案。目前,在业内来说应该还没有特别完美的分布式事务的解决方案,比如在可靠性、可用性、高性能的前提下还能保证数据的强一致性的解决方案。

RocketMQ的事务消息的解决方案只是在消息实时性要求不高的场景中来保证数据的最终一致性。

RocketMQ的事务消息采用了2PC:二阶段提交(英语:Two-phase Commit),并且引入了事务回查(也有叫回溯、反查)的机制来保证。

2PC

关于二阶段提交算法,这里直接引用一下维基百科的介绍:

第一阶段(提交请求阶段)

  1. 协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。
  2. 参与者节点执行询问发起为止的所有事务操作,并将Undo信息Redo信息写入日志。
  3. 各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个"同意"消息;如果参与者节点的事务操作实际执行失败,则它返回一个"中止"消息。

有时候,第一阶段也被称作投票阶段,即各参与者投票是否要继续接下来的提交操作。

第二阶段(提交执行阶段)

成功

当协调者节点从所有参与者节点获得的响应消息都为"同意"时:

  1. 协调者节点向所有参与者节点发出"正式提交"的请求。
  2. 参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
  3. 参与者节点向协调者节点发送"完成"消息。
  4. 协调者节点收到所有参与者节点反馈的"完成"消息后,完成事务。

失败

如果任一参与者节点在第一阶段返回的响应消息为"终止",或者协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:

  1. 协调者节点向所有参与者节点发出"回滚操作"的请求。
  2. 参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
  3. 参与者节点向协调者节点发送"回滚完成"消息。
  4. 协调者节点收到所有参与者节点反馈的"回滚完成"消息后,取消事务。

有时候,第二阶段也被称作完成阶段,因为无论结果怎样,协调者都必须在此阶段结束当前事务。

但是这样直接对着理解RocketMQ的消息可能有点晦涩。RocketMQ的实现简单来说如下:

1. 第一阶段,预提交:producer发送一条Half消息(半消息,该消息此时对消费端不可见,但该消息体内容是业务侧实际要发送的消息内容)

2. 执行本地事务逻辑

3. 第二阶段:如果第2步执行成功,则提交第1步发送的半消息,此时消息对消费端可见,否则第2步执行失败,则回滚第1步发送的消息(并不是真正的撤销消息体,只需要保证那条消息永远对消费端不可见即可)。

事务回查

事务回查是为解决第2阶段消息发送失败引入的一种机制。因为在第2阶段,提交或者回滚消息的时候,不可避免地,可能由于网络原因,提交/回滚消息发送失败,或者当本地事务执行完成,当前producer实例宕机或重启导致第2阶段未成功执行等各种意外出现。如果producer的commit/rollback消息不能成功发送到服务端broker,则broker便不知道第一阶段发送的半消息是否应该提交对消费端可见还是回滚状态,对消费端不可见。

事务回查就是为了解决这种第二阶段消息失败的场景。当producer发送第一阶段的半消息后,没能及时且成功的提交第二阶段的消息,broker便会每隔一分钟询问producer的某一个实例该事务是否成功,应当commit还是rollback,这个回查次数默认最多15次,如果15次后还没有得到正确的事务状态,就直接回滚消息。所以这里有两个关键点需要注意:

  1. 本地事务执行完成,应当在外部共享介质中保存事务执行状态,比如在数据库保存当前消息的事务ID的状态是成功还是失败,事务回查的时候可以根据该事务ID查询本次的本地事务的执行结果来判断消息应该提交还是回滚。
  2. 同一生产组的producer实例应该大于1,因为同一个生产组下的producer表示同一类实例,当事务回查的时候,broker会选取该生产组的下某个实例进行询问。如果生产组的producer实例只有一个,第二阶段的消息是因为网络原因导致失败,随后网络恢复正常,则回查的时候,broker还会与当前producer通信,但如果是因为该节点宕机导致的话,如果当前生产组下没有其它事务,事务回查一直获取不到正确的事务状态,达到最大次数后便要回滚消息,为了保证高可用,建议节点数大于1。

引入事务回查后,事务消息执行流程如下:

使用场景要求

  • 消息实时性要求不高
  • 可以接受最终一致性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不识君的荒漠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值