前言:RocketMQ 提供了两种消费模式, PUSH 和 PULL,大多数场景使用的是PUSH模式,这两种模式分别对应的是 DefaultMQPushConsumer 类和DefaultMQPullConsumer 类。 PUSH 模式实际上在内部还是使用的 PULL 方式实现的,通过 PULL 不断地轮询 Broker 获取消息,当不存在新消息时, Broker 会挂起 PULL 请求,直到有新消息产生才取消挂起,返回新消息。故此处主要讲解PUSH模式, 即 DefaultMQPushConsumer。
Consumer主要用于向Broker请求Producer产生的消息,对其进行消费;对于RocketMQ,我们一定很好奇,如何实现分布式的Consumer消费,如何保证Consumer的顺序性,不重复性呢?
(一)启动consumer
初始化DefaultMQPushConsumer,并设置变量值:
1,用consumerGroup为参数初始化DefaultMQPushConsumer 对象,默认初始化MessageQueue分配策略AllocateMessageQueueAveragely 赋值给DefaultMQPushConsumer.allocateMessageQueueStrategy 变量;
2,设置NameServer值
3,保存应用层的topic-subExpression 值
4、调用 DefaultMQPushConsumer.subscribe方法用于构建 Consumer 端的订阅数据 SubscriptionData 对象。
5、设置 Conumser 线程的最大值,最小值,从何处开始消费等调用
6、DefaultMQPushConsumer.registerMessageListener(MessageListener messageListener)方法设置拉取消息后的回调类
启动 DefaultMQPushConsumer
调用 DefaultMQPushConsumer.start 方法中,直接调用DefaultMQPushConsumerImpl.start 方法,大致逻辑如下:
public synchronized void start() throws MQClientException {
//检查 DefaultMQPushConsumerImpl.ServiceState 的状态
switch (this.serviceState) {
//只有状态为 CREATE_JUST 时才启动该 Producer;
case CREATE_JUST:
log.info("the consumer [{}] start beginning. messageModel={}, isUnitMode={}", this.defaultMQPushConsumer.getConsumerGroup(),
this.defaultMQPushConsumer.getMessageModel(), this.defaultMQPushConsumer.isUnitMode());
//将 DefaultMQPushConsumerImpl.ServiceState 置为 start_failed,防止一个进程中重复启动
this.serviceState = ServiceState.START_FAILED;
//检查参数是否正确
this.checkConfig();
//对 DefaultMQPushConsumer.subscription 变量进行的设置,要将此 Map 变量值转换为 SubscriptionData 对象;
this.copySubscription();
//若消息模式为集群模式且实例名( instanceName)等于“ DEFAULT”,则
//取该进程的 PID 作为该 Consumer 的实例名( instanceName);
if (this.defaultMQPushConsumer.getMessageModel() == MessageModel.CLUSTERING) {
this.defaultMQPushConsumer.changeInstanceNameToPID();
}
//创建 MQClientInstance 对象,需要判断是否已经创建
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);
//初始化 PullAPIWrapper 对象
this.pullAPIWrapper = new PullAPIWrapper(
mQClientFactory,
this.defaultMQPushConsumer.getConsumerGroup(), isUnitMode());
//注册 FilterMessageHook 列表,用于消息的过滤;
this.pullAPIWrapper.registerFilterMessageHook(filterMessageHookList);
//若在应用层通过 DefaultMQPushConsumer.setOffsetStore
if (this.defaultMQPushConsumer.getOffsetStore() != null) {
this.offsetStore = this.defaultMQPushConsumer.getOffsetStore();
} else {
switch (this.defaultMQPushConsumer.getMessageModel()) {
//若消息模式是广播( BROADCASTING),则初始化LocalFileOffsetStore 对象并赋值给
case BROADCASTING:
this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
break;
//若消息模式是集群( CLUSTERING),则初始化 RemoteBrokerOffsetStore 对象并赋值给DefaultMQPushConsumerImpl.offsetStore 变量
case CLUSTERING:
this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
break;
default:
break;
}
}
//将本地的 offsets.json 文件内容加载到 LocalFileOffsetStore.offsetTable 变量中
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());
}
/*
ConsumeMessageConcurrentlyService 类的 start 方法没有实现任何业务逻辑;
ConsumeMessageOrderlyService 的 start 方法的处理逻辑是当消息模式为
集群模式时,设置定时任务每隔 20 秒调用一次ConsumeMessageOrderlyService.lockMQPeriodically()方法,
*/
this.consumeMessageService.start();
//将 DefaultMQPushConsumerImpl 对象在 MQClientInstance 中注册
boolean registerOK = mQClientFactory.registerConsumer(this.defaultMQPushConsumer.getConsumerGroup(), this);
//说明在一个客户端的一个进程下面启动多个Consumer 时 consumerGroup 名字不能一样,否则无法启动;
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);
}
//启动 MQClientInstance 对象
mQClientFactory.start();
log.info("the consumer [{}] start OK.", this.defaultMQPushConsumer.getConsumerGroup());
<