RocketMQ源码解析之broker(事务消息处理)

原创不易,转载请注明出处


前言

我们在《RocketMQ源码解析之消息生产者(事务消息原理篇)》一文中介绍过RocketMQ事务消息的原理,但是主要是介绍的消息生产者使用事务消息最佳姿势以及事务消息设计思想,并没有对broker处理事务消息这块进行深入的解读,然后本文就是为了补上broker对事务消息处理这块的,主要是先介绍broker对于事务消息处理的流程,之后就是进行源码剖析。

1.broker事务消息处理流程

之前介绍过RocketMQ对于事务消息整体运行原理,我们这里再介绍下,就是消息生产者先发送事务消息(这个消息是同步的),在分布式事务里面也就是try阶段,broker 收到消息生产者发送过来的消息之后,就会将消息存入一个half topic的消息队列中,这个时候对消息生产者是不可见的,broker 将消息存储结果返回,接着就是执行消息生产者的本地事务了,如果成功,就会告诉broker 进行事务提交,这个时候broker 就会将之前存在half topic 消息队列中的消息取出来,然后重新放到原来要发送的消息队列中,并关于之前half topic 消息队列中的这个消息删除状态写入op half topic这个消息队列中,如果是需要回滚,这个时候也是一样将关于之前half topic 消息队列中的这个消息删除状态写入op half topic这个消息队列中,如果broker 没有收到本地事务执行结果也没有问题,broker 会不停的发送消息来询问消息生产者你这个消息对应的本地事务执行怎么样了,默认是发送15,就是这个样子。

这里我们需要将broker做的事情拿出来说说

  1. 接收事务消息,存入half topic中去
  2. 提交事务,回滚事务
  3. 检查本地事务执行结果

详细介绍一下每阶段处理是怎样做的。

  1. 接收事务消息,存入half topic 中去,这个其实跟发送普通消息差不多,只不过它这里要将原来的topic ,queueId 换成事务消息的,也就是half topic ,queueId 是half topic 里面queue的id。
  2. 本地事务执行完成之后,会告诉一下broker 某个事务消息的本地执行结果,如果是提交事务的话,就是将原来存在half topic 中的事务消息取出来,换成原来的 topic 与queueId,接着就是将消息写入commitlog中,存入成功之后,将这个事务消息的执行结果写入到这个op half topic 中,这步操作就是为了本地事务消息检查器找出那种还没有确认的消息,其实存入原来topic 之后,broker的reput线程就可以将这个消息在commitlog的位置写到对应queue中了,关于是怎样reput的可以看下《RocketMQ源码解析之broker消息存储流程(Reput ConsumeQueue)》(这里现在不看也可以,不影响本文阅读)消息消费就可以拿这个消息消费了,如果是回滚消息的话,只是将这个事务消息的执行结果写入到这个op half topic 中,对于消息消费者而言,你不写回原来的topic中,消息消费者是不可见的,随着清理时间到了,直接就可以清理了。
  3. broker 会有一个后台线程不停的检查那些没有告诉broker 本地事务执行结果的事务消息,然后回调消息生产者问问这个事务消息对应的本地事务执行如何了,是commit还是rollback。这里这个线程是60s检查一次,然后检查写入half topic 超过6s还没告诉本地事务执行结果的消息。

2.源码分析

2.1 broker事务消息处理组件初始化

这块主要是介绍下broker 处理事务消息的几个组件
在这里插入图片描述
我这里贴了初始化的代码
首先第一行是先使用spi(这个spi是RocketMQ自己处理过的)获取META-INF/service/org.apache.rocketmq.broker.transaction.TransactionalMessageService的实现类,默认是没有的,就会走if里面的代码,创建一个TransactionalMessageBridge 这个玩意就是事务消息服务组件用来与存储器交互使用的,可以从名字上看出来Bridge ,再就是创建事务消息服务组件了,然后也是使用spi获取AbstractTransactionalMessageCheckListener 的实现类,这就是事务消息检查器找出没有本地事务执行结果的消息后,会通知这个监听器,这个监听器来进行响应的处理,这个也是没有,就会创建一个默认的,这个默认的就会发送消息询问消息生产者。最后就是事务消息检查服务的创建了,它其实就是一个线程。
在这里插入图片描述
在这里插入图片描述

2.2 接收事务消息

接收事务消息其实就跟接收普通的消息一样,但是它有个关于事务消息的flag。
在这里插入图片描述
在SendMessageProcessor这个processor中处理发送消息命令的,收到消息后,会有关于事务消息的判断,我们从上面截图代码也可以看出来,事务消息与普通消息走了不同的分支,事务消息找到了这个事务消息服务组件来处理的,调用prepareMessage方法。
在这里插入图片描述
这里事务消息服务组件找到这个Bridge 来存储事务消息
在这里插入图片描述
这里就是调用消息存储器,来存储消息,但是存储消息之前我们可以看到有parseHalfMessageInner 方法处理了一下消息,这个就是RocketMQ事务消息实现的精髓
在这里插入图片描述
前两行就是将 这个消息原来的topic ,与queueId 写入到消息的属性中,属性key分别是REAL_TOPICREAL_QID
接着就是为这个消息重新设置topic , RMQ_SYS_TRANS_HALF_TOPIC就是我们上面一直说的half topic,重新设置了queueId是0 ,也就是一个queue。好了关于消息怎样写入commitlog中的,我们这里就不做介绍了,可以看下《RocketMQ源码解析之broker消息存储流程(PutMessage)

2.3 提交事务或者是回滚事务

提交事务或者回滚事务是由EndTransactionProcessor 这个processor来处理的,我们看下是怎样处理的
在这里插入图片描述
首先是拿到请求,然后进行消息头的解码工作,接着就是判断broker 的角色了,slave角色是不允许做这种写入操作的,只有master 才可以进行消息写相关的操作,但是slave是可以读的,到我们介绍消息消费者消费的时候就能知道这个消息消费者可以去slave获取消息的,也就是实现所谓的读写分离。
接着往下其实就是一堆判断打印日志了,我们这里直接略过
在这里插入图片描述
这一块代码就是关于事务消息commit与rollback的执行全貌了。
如果是commit操作,先调用事务消息服务组件的commitMessage方法,这个其实就是找到Bridge获取之前half存的那个消息。
在这里插入图片描述
获取到存储到half topic那个消息之后就是检查消息,然后将half阶段的消息转成最初的那个消息,我们来看下
在这里插入图片描述
可以看到,这里新建一个消息,然后东西挪到新消息中,最最最重要的是还原了消息之前的topic与queueId。
再接着往下看就是设置一堆别的属性了,包括在half阶段存在commitlog中的offset,消息的存储时间,消息事务状态,queueoffset,之后就是调用了sendFinalMessage 方法
在这里插入图片描述
这个sendFinalMessage 方法,从名字上也能看出来,就是发送最终的消息,其实就是使用存储器组件将新消息写入到commitlog中,接着reput线程就成读到它,然后就是将这个消息放入到对应的comsumeQueue中,消息消费者就能够看到了。
接着往下看,如果写入commitlog成功的话调用事务消息组件删除准备阶段的消息,也就是在half存在的消息,它这里其实不是真删,要是还搞这种随机删除的话,性能损耗是很大的,其实也没必要,因为RocketMQ有自己的删除机制。
我们看下这个所谓的删除是怎样做的。
在这里插入图片描述
调用Bridge,put一个OpMessage,需要注意的是它第二个参数,removetag ,移除标识
在这里插入图片描述
其实就是创建一个queue,topic 就是half topic ,broker就是自己这个, queueId就是0 。
在这里插入图片描述
这里就是创建了一个新的消息,topic 是RMQ_SYS_TRANS_OP_HALF_TOPIC这个,然后tag是remove,消息内容是之前的queueOffset。
最后就是将这个op消息写入到存储器里面了,也即是commitlog里面。
好了,到这我们commit消息就介绍完了,我们再来看下rollback
这个rollback也是获取到之前存在half topic 的消息
在这里插入图片描述
接着就是事务消息服务组件删除这个消息了,跟上面的那个一样,我们就不赘述了。

2.4 定时检查本地事务执行结果

这个事务消息检查器我们要从事务消息检查服务说起,它是一个任务,也就是实现runnable接口,里面有个线程,启动的时候,会执行它的run方法
在这里插入图片描述
这个waitForRunning里面最终会走onWaitEnd方法
在这里插入图片描述
在这里插入图片描述
这个onWaitEnd 方法里面先是获取了一个timeout值,其实就是消息存入half topic后没有本地事务执行结果会检查,这里默认是6s。接着就是最大检查次数,默认是15次,最后就是调用事务消息服务组件check方法了。这个方法其实就是对比half topic 与那个op half topic 里面的消息,然后那种找出那种6s后还没有本地消息执行结果的,也就是没有在op half topic 删除的消息。找到之后,就会重新塞入到half topic中一次,接着就是通知消息生产者,问问消息生产者本地消息执行ok了么我们这里看下核心的代码就可以了
在这里插入图片描述
先说下它为什么要重新塞入消息到half topic中,其实与它检查机制有关,它从half topic拿出一批消息,然后就会认为这批消息已经被它消费了,比对完成后会将消费offset 存入一个consumeOffset组件中,这个组件就是专门管着记录消息消费者消费offset的,下次再检查就会从consumeOffset组件中获取offset,从这个offset开始检查,如果你这个消息之后还是没有本地事务执行结果的话,这个消息下次就不会被检查到了,也就是没有所谓15次检查通知这一说了,它新put 进去之后,下次还能检查到。
接着就是通知这个listener了,我们看下它做了啥,其实就是询问消息生产者本地事务执行ok了么
在这里插入图片描述
生成一个任务放到线程池中
在这里插入图片描述
这块代码其实就是封装请求消息头,然后发送消息了,这里需要注意的有一点就是获取可用channel的时候,是根据生产者组来获取的,不是这个消息原来是哪个消息生产者发送的,就找谁去check去,而是找这个组里面的一个生产者检查,这里就需要注意了。

总结

本文主要是介绍了broker 对于事务消息的处理流程,先介绍了事务消息的整体执行流程,之后详细介绍了broker 是怎样处理事务消息的,就是上面那三步,只要记住那三步,面试RocketMQ事务消息原理这块就没有什么问题了,最后我们分别剖析了一下源码对于事务消息处理具体的实现。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页