閱讀須知
- 文章中使用/* */註釋的方法會做深入分析
正文
Consumer 的啓動流程和 Producer 的啓動流程有很多複用的部分,前面我們已經分析過 Producer 的啓動流程,複用部分這裏不再重複。
使用 RocketMQ Consumer 消費消息的簡單配置如下,:
<bean id="rocketmqConsumer" class="org.apache.rocketmq.client.consumer.DefaultMQPushConsumer" init-method="start"
destroy-method="shutdown">
<property name="consumerGroup" value="${rocketmq.consumer.group}"/>
<property name="namesrvAddr" value="${rocketmq.nameserver.address}"/>
<property name="messageListener" ref="orderMessageListener"/>
<property name="subscription">
<map>
<entry key="${rocketmq.topic}" value="*"/>
</map>
</property>
</bean>
我們看到配置中 init-method 配置了 start 方法,所以在 bean 的初始化過程中會調用 DefaultMQPushConsumer 的 start 方法:
public void start() throws MQClientException {
setConsumerGroup(NamespaceUtil.wrapNamespace(this.getNamespace(), this.consumerGroup));
/* 啓動 */
this.defaultMQPushConsumerImpl.start();
if (null != traceDispatcher) {
try {
traceDispatcher.start(this.getNamesrvAddr(), this.getAccessChannel());
} catch (MQClientException e) {
log.warn("trace dispatcher start failed ", e);
}
}
}
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();
// 複製訂閱信息,複製消息監聽器
this.copySubscription();
// 默認爲集羣模式
if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) {
this.defaultMQPushConsumer.changeInstanceNameToPID();
}
// 獲取並創建 client,與 Producer 啓動複用同樣的方法
this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQPushConsumer, this.rpcHook);
this.rebalanceImpl.setConsumerGroup(this.defaultMQPushConsumer.getConsumerGroup());
this.rebalanceImpl.setMessageModel(this.defaultMQPushConsumer.getMessageModel());
this.rebalanceImpl.setAllocateMessageQueueStrategy(this.defaultMQPushConsumer.getAllocateMessageQueueStrategy());
this.rebalanceImpl.setmQClientFactory(this.mQClientFactory);
this.pullAPIWrapper = new PullAPIWrapper(
mQClientFactory,
this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode());
this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList);
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:
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;
this.consumeMessageService =
new ConsumeMessageOrderlyService(this, (MessageListenerOrderly) this.getMessageListenerInner());
} else if (this.getMessageListenerInner() instanceof MessageListenerConcurrently) {
this.consumeOrderly = false;
this.consumeMessageService =
new ConsumeMessageConcurrentlyService(this, (MessageListenerConcurrently) this.getMessageListenerInner());
}
// 消費服務啓動,對於併發消息消費服務,會啓動定時清理垃圾消息服務;順序消息我們單獨分析
this.consumeMessageService.start();
// 添加 consumerGroup 和消費者實例的映射
boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this);
if (!registerOK) {
this.serviceState = ServiceState.CREATE_JUST;
// 添加映射失敗停止消費服務,失敗的原因可能爲同一個 consumerGroup 被多次添加,這是不允許的
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);
}
// 啓動客戶端實例,和 Producer 啓動複用相同的流程
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;
}
// 更新topic路由信息,主要通過向 NameServer 發送 GET_ROUTEINTO_BY_TOPIC 請求來獲取最新的 topic 路由信息,然後更新內部消費者的訂閱信息和生產者的發佈信息
this.updateTopicSubscribeInfoWhenSubscriptionChanged();
// 向 Broker 發送 CHECK_CLIENT_CONFIG 命令檢查客戶端的配置,主要檢查訂閱表達式的正確性,Broker 處理對應的命令的處理器爲 ClientManageProcessor
this.mQClientFactory.checkClientInBroker();
// 向所有 Broker 發送心跳請求
this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
// 啓動完成立刻做一次 rebanlance
this.mQClientFactory.rebalanceImmediately();
}
rebanlance 是消息消費過程中一個重要的流程,我們單獨分析。Consumer 啓動流程和 Producer 啓動流程複用了很多邏輯,需要結合兩篇文章一起來看。