RocketMQ 消费者启动源码解析

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();
}
  1. 检查消费者参数
  2. 拷贝订阅信息 如果是集群消费模式 将会给每个主题都创建一个重试主题 %RETRY% + topic
  3. 如果是集群消费模式将消费者的实例名转成pid
  4. 创建或者获取MQClientInstance实例 同一个jvm下的消费者默认公用一个MQClientInstance实例
  5. 给rebalanceImpl设置消费组、消费模式、队列分配策略、MQClientInstance实例
  6. 创建拉取消息api包装类
  7. 设置offsetStore 用来保存当前消费者的消费进度
  8. 加载消费进度 广播模式从文件中加载出消费进度、集群模式空实现
  9. 创建消息消费服务,该服务可以提交消息拉取任务,并根据消息监听器来处理消息
  10. 启动消息消费服务
  11. 注册消费者,groupName需要唯一,如果之前同一个jvm中已经注册了相同的groupName,那么将抛出异常
  12. 启动mQClientFactory
  13. 跟新消费者的主题路由信息 获取该主题在哪些broker上,broker的ip,以及主题有哪些队列
  14. 发送心跳信息 将消费者和生产者元信息都发给broker
  15. 唤醒消费者负载均衡线程 立刻进行一次负载均衡启动消息拉取
posted @ 2020-08-21 15:51  鹿慕叶  阅读(624)  评论(0编辑  收藏  举报