rocketMQ的consumer 源码分析
概述
消息消费是以消费组为单位,消费组可以订阅多个主题。每个消费组有两种模式,1.广播模式,一条消息被集群内的所有消费者消费;2.集群模式,同一条消息只能被所有消费者消费一次。消息传递的方式有两种,推模式和拉模式。推模式是在拉模式上包了一层。
广播模式是比较简单,我们主要分析分析集群模式。同时选取拉模式的代码。
集群模式下首先想到的是下面几个问题
一个消费组有多个消费者线程,一个主题存在有多个消费队列,消费者之间如何做消息队列的负载?如何不被重复消费?如何支持顺序消费?线程是如何分工合作的?
消费者的启动流程
之前的书上都是讲MQPullConsumer
相关,在新版本中其实现类已经不建议使用了,取而代之的是LitePullConsumer
接口以及其实现类DefaultLitePullConsumer
。
消息消费的启动流程在,DefaultLitePullConsumerImpl
的start方法中,请看代码中的解释
this.serviceState = ServiceState.START_FAILED;
this.checkConfig();
//集群模式
if (this.defaultLitePullConsumer.getMessageModel() == MessageModel.CLUSTERING) {
this.defaultLitePullConsumer.changeInstanceNameToPID();
}
//注册consumer group
initMQClientFactory();
//设置负载均衡 所需的 consumer group信息,设置负载分配的策略
initRebalanceImpl();
//注册拉取消息的过滤器
initPullAPIWrapper();
//初始化消息消费的偏移量,广播模式从本地获取,集群模式从远程获取
initOffsetStore();
/**
1.获取namesrv
2.启动channel
3.启动各种定时任务,定时获取namesrv;定时更新路由信息;心跳保持;持久化消费者偏移量;线程池调整;
4.拉取消息线程启动
5.重新负载线程启动
6.初始化producer
*/
mQClientFactory.start();
//启动一个定时任务,监听message queue的变化
startScheduleTask();
this.serviceState = ServiceState.RUNNING;
operateAfterRunning();
break;
需要注意的一点就是启动的很多对象都是全局唯一的,使用了synchronized相关的语法,以及使用枚举类去做状态机的流转。
消息拉取
上文的启动流程中可以知道,负责拉取消息的一个独立的线程,PullMessageService
继承自ServiceThread
,通过run方法可以知道其实现机制,具体代码如下。
public void run() {
log.info(this.getServiceName() + " service started");
while (!this.isStopped()) {
try {
pullRequest pullRequest = this.pullRequestQueue.take();
this.pullMessage(pullRequest);
} catch (InterruptedException ignored) {
} catch (Exception e