全网最详细的 RocketMQ 源码解读方案(下)赶紧收藏

一、启动

DefaultMQPushConsumer.start方法

启动过程不用太过关注,有个概念就行,然后客户端启动的核心是 mQClientFactory 主要是启动了一大堆的服务。这些服务可以结合具体场景再进行深入。例如pullMessageService主要处理拉取消息服务,rebalanceService主要处理客户端的负载均衡。

二、消息拉取

拉模式:PullMessageService

PullRequest里有messageQueue和processQueue,其中messageQueue负责拉取消息,拉取到后将消息存入processQueue,进行处理。存入后就可以清空 messageQueue,继续拉取了。       

全网最详细的 RocketMQ 源码解读方案(下)赶紧收藏_第1张图片

三、客户端负载均衡策略

在消费者示例的start方法中,启动RebalanceService,这个是客户端进行负载均衡 策略的启动服务。他只负责根据负载均衡策略获取当前客户端分配到的 MessageQueue示例。五种负载策略,可以由Consumer的allocateMessageQueueStrategy属性来选 择。最常用的是AllocateMessageQueueAveragely平均分配和 AllocateMessageQueueAveragelyByCircle平均轮询分配。

平均分配是把MessageQueue按组内的消费者个数平均分配。而平均轮询分配就是把MessageQueue按组内的消费者一个一个轮询分配。

例如,六个队列q1,q2,q3,q4,q5,q6,分配给三个消费者c1,c2,c3

平均分配的结果就是: c1:{q1,q2},c2:{q3,q4},c3{q5,q6}

平均轮询分配的结果就是:c1:{q1,q4},c2:{q2,q5},c3:{q3,q6}

四、并发消费与顺序消费的过程

消费的过程依然是在DefaultMQPushConsumerImpl的 consumeMessageService中。他有两个子类 ConsumeMessageConcurrentlyService和ConsumeMessageOrderlyService。其中最主要的差别是ConsumeMessageOrderlyService会在消费前把队列锁起来, 优先保证拉取同一个队列里的消息。消费过程的入口在DefaultMQPushConsumerImpl的pullMessage中定义的 PullCallback中。

五、延迟消息

功能回顾

我们这里,就用一个典型的延迟消息的流程,来把上面看到的各个组件,结合一 下。

延迟消息的核心使用方法就是在Message中设定一个MessageDelayLevel参数,对应18个延迟级别。然后Broker中会创建一个默认的Schedule_Topic主题,这个主题下有18个队列,对应18个延迟级别。消息发过来之后,会先把消息存入 Schedule_Topic主题中对应的队列。然后等延迟时间到了,再转发到目标队列,推送给消费者进行消费。

整个延迟消息的实现方式是这样的:       

全网最详细的 RocketMQ 源码解读方案(下)赶紧收藏_第2张图片

源码重点

延迟消息的处理入口在scheduleMessageService这个组件中。他会在broker启动时也一起加载。

1、消息写入

代码见CommitLog.putMessage方法。

在CommitLog写入消息时,会判断消息的延迟级别,然后修改Message的Topic和 Queue,达到转储Message的目的。

2、消息转储到目标Topic

这个转储的核心服务是scheduleMessageService,他也是Broker启动过程中的一个功能组件、 然后ScheduleMessageService会每隔1秒钟执行一个executeOnTimeup任务,将消息从延迟队列中写入正常Topic中。代码见ScheduleMessageService中的 DeliverDelayedMessageTimerTask.executeOnTimeup方法。这个其中有个需要注意的点就是在ScheduleMessageService的start方法中。有一 个很关键的CAS操作:

if (started.compareAndSet(false, true)) {

这个CAS操作保证了同一时间只会有一个DeliverDelayedMessageTimerTask执行。保证了消息安全的同时也限制了消息进行回传的效率。所以,这也是很多互联网公司在使用RocketMQ时,对源码进行定制的一个重点。

六、消费者部分小结

RocketMQ消息消费方式分别为集群模式、广播模式。

消息队列负载由RebalanceService线程默认每隔20s进行一次消息队列负载,根据当前消费者组内消费者个数与主题队列数量按照某一种负载算法进行队列分配,分配原则为同一个消费者可以分配多个消息消费队列,同一个消息消费队列同一个时间只会分配给一个消费者。

消息拉取由PullMessageService线程根据RebalanceService线程创建的拉取任务进行拉取,默认每次拉取32条消息,提交给消费者消费线程后继续下一次消息拉取。如果消息消费过慢产生消息堆积会触发消息消费拉取流控。并发消息消费指消费线程池中的线程可以并发对同一个消息队列的消息进行消费,消费成功后,取出消息队列中最小的消息偏移量作为消息消费进度偏移量存储在于消息消费进度存储文件中,集群模式消息消费进度存储在Broker(消息服务器), 广播模式消息消费进度存储在消费者端。

RocketMQ不支持任意精度的定时调度消息,只支持自定义的消息延迟级别,例如 1s、2s、5s等,可通过在broker配置文件中设置messageDelayLevel。顺序消息一般使用集群模式,是指对消息消费者内的线程池中的线程对消息消费队列只能串行消费。与并发消息消费最本质的区别是消息消费时必须成功锁定消息消费队列,在Broker端会存储消息消费队列的锁占用情况

七、源码解读小结

关于RocketMQ的源码部分,我们就带大家解读到这里。到目前为止,几个核心的流程我们已经解读完成了,我们按照由大到小,由粗到细的方式对几条主线进行了解读。通过解读源码,我们可以对之前提到的各种高级特性有更深入的理解。对有些有争议的问题,带着问题来源码中找答案是最好的。例如我们经常有人讨论NameServer全部挂了之后,生产者和消费者是否能够用他本地的缓存继续工作一段时间?这样的一些问题,看过源码之后是不是有更清晰的了解?至于其他的代码,大家也可以按照自己的关注点,以业务线的方式来逐步解读。

最后将我们今天解读的部分源码整理成了一个大图,可供参考。截图可能不是很清晰,需要高清图片和学习资料的可以加微信领取,备注“222”

全网最详细的 RocketMQ 源码解读方案(下)赶紧收藏_第3张图片

你可能感兴趣的:(java,面试,学习,经验分享)