RocketMQ 事务消息

RocketMQ 官方发布了 4.3.0 版本,此版本解决了 RocketMQ 对事务的支持,这一重大更新对 RocketMQ 至关重要 。 本章将基于 RocketMQ 官方最新 4.3.0 版本,对其事务消息的实现原理进行深入探讨。 主要内容如下 。
·事务消息实现思想
·事务消息发送流程
·事务消息提交或回滚
·事务消息回查事务状态
1 事务消息实现思想
RocketMQ 事务消息的实现原理基于两阶段提交和定时事务状态回查来决定消息最终是提交还是回滚,交互设计如图 8-1 所示 。
1 )应用程序在事务内完成相关业务数据落库后,需要同步调用 RocketMQ 消息发送接
口,发送状态为 prepare 的消息 。 消息发送成功后, RocketMQ 服务器会回调RocketMQ 消息发送者的事件监听程序,记录消息的本地事务状态,该相关标记与本地业务操作同属一个事务,确保消息发送与本地事务的原子’性 。
2) RocketMQ 在收到类型为 prepare 的消息时, 会首先备份消息的原主题与原消息消费
队列,然后将消息存储在主题为 RMQ_SYS_TRANS_HALF_TOPIC 的消息消费队列中。
3) RocketMQ 消息服务器开启一个定时任务,消费 RMQ_SYS_TRANS_HALF_TOPIC
的消息,向消息发送端(应用程序)发起消息事务状态回查,应用程序根据保存的事务状态回馈消息服务器事务的状态(提交、回滚、未知),如果是提交或回滚, 则消息服务器提交或回滚消息,如果是未知,待下一次回查, RocketMQ 允许设置一条消息的回查间隔与回查次数,如果在超过回查次数后依然无法获知消息的事务状态, 则默认回滚消息 。

2 事务消息发送流程
RocketMQ 事务消息发送者为 org.apache.rocketmq.client.producer.TransactionMQProducer 。
1 TransactionMQProducer
1 ) TransactionListener transactionListener : 事务监听器,主要定义实现本地事务状态执行 、本地事务状态回查两个接 口 。
2 ) ExecutorService executorService : 事务状态 回查异步执行线程池 。
2. Transactionlistener
1 ) LocalTransactionState executeLocalTransaction (final Message msg, final Object arg ) :执行本地事务 。
2 ) LocalTransactionState checkLocalTransaction (final MessageExt msg): 事务消息状态回查 。
 

如果事件监听器为空, 则直接返回异常,最终调用 DefaultMQProducerlmpl的sendMessagelnTransaction 方法 。
Step1:首先为消息添加属性, TRAN_MSG 和 PGROUP ,分别表示消息为 prepare 消息 、消息所属消息生产者组 。 设置消息生产者组的目的是在查询事务消息本地事务状态时,从该生产者组中随机选择一个消息生产者即可,然后通过同步调用方式向RocketMQ 发送消息
St ep2 : 根据消息发送结果执行相应 的操作 。
1 ) 如果消息发送成功 , 则执行 TransactionListener#executeLocalTransaction 方法,该方法的职责是记录事务消息 的本地事务状态, 例如可以通过将消息唯一 ID 存储在数据中 ,并且该方法与业务代码处于同一个事务,与业务事务要么一起成功,要么一起失败 。 这里是事务消息设计的关键理念之一, 为后续的事务状态 回查提供唯一依据 。
2 )如果消息发送失败 ,则设置本次事务状态为 LocalTransactionState.ROLLBACK_MESSAGE 。
Step3 :结束事务 。 根据第二步返回的事务状态执行提交、 回 滚或暂时不处理事务 。
1) LocalTransactionState.COMMIT_MESSAGE : 提交事务 。
2 ) LocalTransactionState.ROLLBACK_MESSAGE :回滚事务 。
3) LocalTransactionState .UNKNOW : 结束事务 ,但不做任何处理。

事务消息发送的整体流程就介绍到这里了 , 接下来我们再重点介绍一下 prepare 消息发送的全过程 。
在消息发送之前,如果消息为prepare 类型,则设置消息标准为 prepare消息类型,方便消息服务器正确识别事务类型的消息 。

Broker 端在收到消息存储请求时 ,如果消息为prepare 消息,则执行prepareMessage方法,否则走普通消息的存储流程 。

这里是事务消息与非事务消息发送流程 的 主要区别 , 如果是事务消 息则备份消息的原主题与原消息消费 队列, 然后将主题变更为 RMQ_SYS_TRANS_HALF TOPIC ,消 费队列变更为 0 , 然后消息按照普通消息存储在 commitlog 文件进而转发到 RMQ SYS_TRANS_HALF_TOPIC 主题对应 的 消息消费队列 。 也就是说,事务消息在未提交之前并不会存入消息原有主题 , 自 然也不会被消费者消费 。 既然变更了主题, RocketMQ 通 常会采用定时任务(单独的线程 )去消 费该主题 , 然后将该消 息在满足特定条件下恢复消息主题,进而被消费者消费 。 读者对这种实现应该并不陌生 , 它与 RocketMQ 定时消息的处理过程如出一辙 。
RocketMQ 事务发送流程图如 图 8 - 3 所示 。
接下来重点分析一下调用结束事务 DefaultMQProducerlmp l#endTransaction 。

3 提交或回滚事务
本节继续探讨两阶段提交的第二个阶段: 提交或回滚事务 。
根据消息所属 的消息队列获取 Broker 的 IP与端口 信息 ,然后发送结束事务命令 ,其关键就是根据本地执行事务的状态分别发送提交 、 回滚或 “不作为”的命令 。 Broker 服务端的结束事务处理器为 : EndTransactionProcessor。
如果结束事务动作为提交事务,则执行提交事务逻辑,其关键实现如下 。
1 )首先从结束事务请求命令中获取消息的物理偏移量(commitlogOffset),其实现逻辑由 TransactionalMessageService#commitMessage 实现 。
2 )然后恢复消息的主题 、 消费队列,构建新的消息对象,由TransactionalMessageService#endMessageTransaction 实现 。
3 )然后将消息再次存储在 commitlog 文件中,此时的消息 主题则为业务方发送的消
息,将被转发到对应的消息消费队列,供消息消费者消费,其实现由TransactionalMessageService#sendFinalMessage 实现 。
4 )消息存储后,删除 prepare 消息,其实现方法并不是真正的删除,而是将 prepare消息存储到 RMQ_SYS_TRANS_OP_HALF_TOPIC 主题中,表示该事务消息(prepare 状态的消息)已经处理过(提交或回滚),为未处理的事务进行事务回查提供查找依据。事务的回滚与提交的唯一差别是无须将消息恢复原主题,直接删除 prepare 消息即可,同样是将预处理消息存储在 RMQ_SYS_TRANS_OP_HALF_TOPIC 主题中,表示已处理过该消息 。
 

4 事务消息回查事务状态
上节重点梳理了 RocketMQ 基于两阶段协议发送与提交回滚消息,本节将深入学习事
务状态、消息回查,事务消息存储在消息服务器时主题被替换为RMQ_SYS_TRANS_HALF_TOPIC,执行完本地事务返回本地事务状态为 UN_KNOW 时,结束事务时将不做任何处理,而是通过事务状态定时回查以期得到发送端明确的事务操作(提交事务或回滚事务) 。
RocketMQ 通过 TransactionalMessageCheckService 线程定 时去检测 RMQ_SYS_TRANS_HALF_TOPIC 主题中的消息,回查消息的事务状态 。 TransactionalMessageCheckService 的检测频率默认为 1 分钟,可通过在 broker.conf文件中设置 transactionChecklnterval 来改变默认值,单位为毫秒。


TransactionalMessageCheckService#onWaitEnd

transactionTimeOut :事务的过期时间 只有当消息的存储时间加上过期时间大于系统
当前时间时,才对消息执行事务状态回查 ,否则在下一次周期中执行事务回查操作 。
transactionCheckMax : 事务 回 查最大检测次数,如果超过最大检测次数还是无法获知消息的事务状态, RocketMQ 将不会继续对消息进行事务状态回查,而是直接丢弃即相当于回滚事务 。
事务消息的处理涉及如下两个主题。
RMQ SYS_ TRANS_ HALF_TOPIC: prepare 消息的主题,事务消息首先进入到该主题 。
RMQ_SYS_TRANS_OP_HALF_TOPIC : 当消息服务器收到 事务消息 的提交或因滚请求后, 会将消息存储在该主题下 。
首先构建事务状态回查请求消息,核心参数包含消息 offsetld 、 消息 ID (索引)、 消息事务 ID、事务消息队列中的偏移量、消息主题 、 消息队列 。 然后根据消息的生产者组,从中随机选择一个消息发送者 。 最后 向消息发送者发送事务回查命令 。
事务回查命令的最终处理者为 ClientRemotingProssor 的 processRequest 方法,最
终将任务提交到TransactionMQProducer的线程池中执行,最终调用应用程序实现的
TransactionListener 的 checkLoca!Transaction 方法 ,返回 事务状态 。 如果事务状态为 Loca lTransactionState#COMMIT_MESSAGE , 则向 消息服务器发送提交事务消息命令 ;如果事务状态为 Loca!TransactionState#ROLLBACK MESSAGE ,则向 Broker 服务器发送回滚事务操作; 如果事务状态为 UNOWN ,则服务端会忽略此次提交 。
5 本章小结
本章重点分析了 RocketMQ 事务消息的实现机制 ,一言以蔽之, RocketMQ 事务消息基于两阶段提交和事务状态回查机制来实现,所谓的两阶段提交,即首先发送 prepare 消息,待事务提交或回滚时发送 commit , rollback 命令 。 再结合定时任务,RocketMQ 使用专门的线程以特定的频率对 RocketMQ 服务器上的 prepare 信息进行处理, 向发送端查询事务消息的状态来决定是否提交或回滚消息 。
 

文章参考:《RocketMQ技术内幕》,作者:丁威,周继锋。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值