RocketMQ 消费者启动源码解析
DefaultMQPushConsumer
引用一段消费者启动代码:
/*
* Instantiate with specified consumer group name.
*/
//① 实例化DefaultMQPushConsumer 参数为groupName 消费组组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
/*
* Specify name server addresses.
* <p/>
*
* Alternatively, you may specify name server addresses via exporting environmental variable: NAMESRV_ADDR
* <pre>
* {@code
* consumer.setNamesrvAddr("name-server1-ip:9876;name-server2-ip:9876");
* }
* </pre>
*/
/*
* Specify where to start in case the specified consumer group is a brand new one.
*/
//设置消费者从何处开始消费,此设置只对该消费组第一次启动的消费者有效
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//不能设置,否则会调整端口...
consumer.setVipChannelEnabled(false);
//设置naveServer地址
consumer.setNamesrvAddr(nameServer);
/*
* Subscribe one more more topics to consume.
*/
//② 设置订阅信息 第一个参数为主题,第二个参数是tag 多个用 || 连接
consumer.subscribe("TopicTest", "*");
/*
* Register callback to execute on arrival of messages fetched from brokers.
*/
//注册消息消费监听器,用来处理消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
/*
* Launch the consumer instance.
*/
//③启动消费者
consumer.start();
System.out.printf("Consumer Started.%n");
① 实例化DefaultMQPushConsumer 参数为groupName 消费组组名
public DefaultMQPushConsumer(final String consumerGroup, RPCHook rpcHook,
AllocateMessageQueueStrategy allocateMessageQueueStrategy) {
this.consumerGroup = consumerGroup;
this.allocateMessageQueueStrategy = allocateMessageQueueStrategy;
defaultMQPushConsumerImpl = new DefaultMQPushConsumerImpl(this, rpcHook);
}
设置groupName,并创建内部的消息消费者实现类defaultMQPushConsumerImpl
② 设置订阅信息 第一个参数为主题,第二个参数是tag 多个用 || 连接
consumer.subscribe("TopicTest", "*");
@Override
public void subscribe(String topic, String subExpression) throws MQClientException {
this.defaultMQPushConsumerImpl.subscribe(topic, subExpression);
}
内部调用defaultMQPushConsumerImpl#subscribe方法,接下去看
public void subscribe(String topic, String subExpression) throws MQClientException {
try {
//构建SubscriptionData 存储路由信息
SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(this.defaultMQPushConsumer.getConsumerGroup(),
topic, subExpression);
//将封装了路由信息的SubscriptionData存到rebalanceImpl中,用map保存
this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData);
if (this.mQClientFactory != null) {
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
}
} catch (Exception e) {
throw new MQClientException("subscription exception", e);
}
}
topic主题、tags标签路由信息被封装进SubscriptionData对象,最终存储到rebalanceImpl中
③启动消费者
consumer.start();
public void start() throws MQClientException {
//启动内部的defaultMQPushConsumerImpl
this.defaultMQPushConsumerImpl.start();
}
启动内部的defaultMQPushConsumerImpl
public synchronized void start() throws MQClientException {
switch (this.serviceState) {
//启动
case CREATE_JUST:
log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(),
this.defaultMQPushConsumer.getMessageModel(), this.defaultMQPushConsumer.isUnitMode());
this.serviceState = ServiceState.START_FAILED;
//①检查消费者参数
this.checkConfig();
//②拷贝订阅信息 如果是集群消费模式 将会给每个主题都创建一个重试主题 %RETRY% + topic
this.copySubscription();
//③如果是集群消费模式将消费者的实例名转成pid
if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) {
//将实例名转成pid
this.defaultMQPushConsumer.changeInstanceNameToPID();
}
//④ 创建或者获取MQClientInstance实例 同一个jvm下的消费者默认公用一个MQClientInstance实例
this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook);
// ⑤ 给rebalanceImpl设置消费组、消费模式、队列分配策略、MQClientInstance实例
this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup());
this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel());
this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer.getAllocateMessageQueueStrategy());
this.rebalanceImpl.setmQClientFactory(this.mQClientFactory);
//⑥ 创建拉取消息api包装类
this.pullAPIWrapper = new PullAPIWrapper(
mQClientFactory,
this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode());
this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList);
//⑦ 设置offsetStore 用来保存当前消费者的消费进度
if (this.defaultMQPushConsumer.getOffsetStore() != null) {
this.offsetStore = this.defaultMQPushConsumer.getOffsetStore();
} else {
switch (this.defaultMQPushConsumer.getMessageModel()) {
case BROADCASTING:
//广播模式使用本地文件存储消费进度
this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
break;
case CLUSTERING:
//集群模式使用broker存储消费进度
this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
break;
default:
break;
}
this.defaultMQPushConsumer.setOffsetStore(this.offsetStore);
}
//⑧ 加载消费进度 广播模式从文件中加载出消费进度、集群模式空实现
this.offsetStore.load();
//⑨ 创建消息消费服务,该服务可以提交消息拉取任务,并根据消息监听器来处理消息
if (this.getMessageListenerInner() instanceof MessageListenerOrderly) {
//顺序消费
this.consumeOrderly = true;
//设置consumeMessageService为顺序消费服务
this.consumeMessageService =
new ConsumeMessageOrderlyService(this, (MessageListenerOrderly) this.getMessageListenerInner());
} else if (this.getMessageListenerInner() instanceof MessageListenerConcurrently) {
//并发消费
this.consumeOrderly = false;
//创建consumeMessageService为并发消费服务
this.consumeMessageService =
new ConsumeMessageConcurrentlyService(this, (MessageListenerConcurrently) this.getMessageListenerInner());
}
//⑩ 启动消息消费服务
this.consumeMessageService.start();
//11 注册消费者,groupName需要唯一,如果之前同一个jvm中已经注册了相同的groupName,那么将抛出异常
boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this);
if (!registerOK) {
this.serviceState = ServiceState.CREATE_JUST;
this.consumeMessageService.shutdown();
throw new MQClientException("The consumer group[" + this.defaultMQPushConsumer.getConsumerGroup()
+ "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
null);
}
//12 启动mQClientFactory
mQClientFactory.start();
log.info("the consumer [{}] start OK.", this.defaultMQPushConsumer.getConsumerGroup());
this.serviceState = ServiceState.RUNNING;
break;
case RUNNING:
case START_FAILED:
case SHUTDOWN_ALREADY:
throw new MQClientException("The PushConsumer service state not OK, maybe started once, "
+ this.serviceState
+ FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
null);
default:
break;
}
//13 跟新消费者的主题路由信息 获取该主题在哪些broker上,broker的ip,以及主题有哪些队列
this.updateTopicSubscribeInfoWhenSubscriptionChanged();
this.mQClientFactory.checkClientInBroker();
//14 发送心跳信息 将消费者和生产者元信息都发给broker
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
//15 唤醒消费者负载均衡线程 立刻进行一次负载均衡启动消息拉取
this.mQClientFactory.rebalanceImmediately();
}
- 检查消费者参数
- 拷贝订阅信息 如果是集群消费模式 将会给每个主题都创建一个重试主题 %RETRY% + topic
- 如果是集群消费模式将消费者的实例名转成pid
- 创建或者获取MQClientInstance实例 同一个jvm下的消费者默认公用一个MQClientInstance实例
- 给rebalanceImpl设置消费组、消费模式、队列分配策略、MQClientInstance实例
- 创建拉取消息api包装类
- 设置offsetStore 用来保存当前消费者的消费进度
- 加载消费进度 广播模式从文件中加载出消费进度、集群模式空实现
- 创建消息消费服务,该服务可以提交消息拉取任务,并根据消息监听器来处理消息
- 启动消息消费服务
- 注册消费者,groupName需要唯一,如果之前同一个jvm中已经注册了相同的groupName,那么将抛出异常
- 启动mQClientFactory
- 跟新消费者的主题路由信息 获取该主题在哪些broker上,broker的ip,以及主题有哪些队列
- 发送心跳信息 将消费者和生产者元信息都发给broker
- 唤醒消费者负载均衡线程 立刻进行一次负载均衡启动消息拉取