RocketMQ集群消费时队列分配

RocketMQ集群消费时队列分配

何时需要消息队列
业务解耦
最终一致性
广播
错峰流控
RocketMQ的核心概念

详情见于文档

PushConsumer调用负载均衡的方法
    public void doRebalance() {

        if (this.rebalanceImpl != null && !this.pause) {

            this.rebalanceImpl.doRebalance(this.isConsumeOrderly());

        }

    }

可以看出真正负载均衡的是rebalanceImpl这个成员变量的工作,在RebelanceImpl类中代码为


public void doRebalance(final boolean isOrder) {

        Map subTable = this.getSubscriptionInner();

        if (subTable != null) {

            for (final Map.Entry entry : subTable.entrySet()) {

                final String topic = entry.getKey();

                try {

                    this.rebalanceByTopic(topic, isOrder);

                } catch (Exception e) {

                    if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {

                        log.warn("rebalanceByTopic Exception", e);

                    }

                }

            }

        }



        this.truncateMessageQueueNotMyTopic();

    }

  1. 获取订阅关系,订阅关系是在consumer.start()中从consumer中复制过来的,订阅关系是以topic为key,tags组成的subscriptionData。

  2. 第二步遍历订阅关系,调用rebalanceByTopc(topic,isOrder)方法,根据topic和isOrder进行负载均衡

  3. 遍历结束,调用truncateMessageQueueNotMyTopic()方法,去除不属于当前consumer的topic对应的消息队列

分析rebalanceByTopic方法


private void rebalanceByTopic(final String topic, final boolean isOrder) {

        switch (messageModel) {

            case CLUSTERING: {

                //SD:集群模式下,从订阅信息表中获取topic对应的所有消息队列

                Set mqSet = this.topicSubscribeInfoTable.get(topic);

                //SD:根据topic和consumerGroupName获取所有相关的consumerId

                //SD:此处如果同一个consumerGroupName下面的订阅关系不一致的话,会导致消息消费失败

                List cidAll = this.mQClientFactory.findConsumerIdList(topic, consumerGroup);

                if (null == mqSet) {

                    if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {

                        log.warn("doRebalance, {}, but the topic[{}] not exist.", consumerGroup, topic);

                    }

                }

                if (null == cidAll) {

                    log.warn("doRebalance, {} {}, get consumer id list failed", consumerGroup, topic);

                }

                if (mqSet != null && cidAll != null) {

                    List mqAll = new ArrayList();

                    mqAll.addAll(mqSet);

                    Collections.sort(mqAll);

                    Collections.sort(cidAll);

                    AllocateMessageQueueStrategy strategy = this.allocateMessageQueueStrategy;

                    List allocateResult = null;

                    //把所有的消息队列按照配置的分配策略进行分配,获取当前consumer获得的消息队列

                    try {

                        allocateResult = strategy.allocate(//

                                this.consumerGroup, //

                                this.mQClientFactory.getClientId(), //

                                mqAll, //

                                cidAll);

                    } catch (Throwable e) {

                        log.error("AllocateMessageQueueStrategy.allocate Exception. allocateMessageQueueStrategyName={}", strategy.getName(),

                                e);

                        return;

                    }



                    Set allocateResultSet = new HashSet();

                    if (allocateResult != null) {

                        allocateResultSet.addAll(allocateResult);

                    }

                    //根据负载均衡的结果更新处理队列,consumer根据消息队列拉取消息

                    //移除不属于当前consumer的队列或者距离上次拉取时间超过最大间隔的队列

                    boolean changed = this.updateProcessQueueTableInRebalance(topic, allocateResultSet, isOrder);

                    if (changed) {

                        log.info(

                                "rebalanced result changed. allocateMessageQueueStrategyName={}, group={}, topic={}, clientId={}, mqAllSize={}, cidAllSize={}, rebalanceResultSize={}, rebalanceResultSet={}",

                                strategy.getName(), consumerGroup, topic, this.mQClientFactory.getClientId(), mqSet.size(), cidAll.size(),

                                allocateResultSet.size(), allocateResultSet);

                        this.messageQueueChanged(topic, mqSet, allocateResultSet);

                    }

                }

                break;

            }

            default:

                break;

        }

    }

默认情况下,使用平均分配消息队列的策略;
分析truncateMessageQueueNotMyTopic方法


private void truncateMessageQueueNotMyTopic() {

        //获取从consumer处copy的订阅关系

        Map subTable = this.getSubscriptionInner();

        //遍历自己的执行队列的消息队列集合

        // 如果目标消息队列的topic不存在当前的订阅关系中,移除这个消息队列

        for (MessageQueue mq : this.processQueueTable.keySet()) {

            if (!subTable.containsKey(mq.getTopic())) {



                ProcessQueue pq = this.processQueueTable.remove(mq);

                if (pq != null) {

                    pq.setDropped(true);

                    log.info("doRebalance, {}, truncateMessageQueueNotMyTopic remove unnecessary mq, {}", consumerGroup, mq);

                }

            }

        }

    }

什么时候负载均衡

在consumer启动的过程中,rebalanceImpl会从consumer处复制订阅关系


public 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();

                }



                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.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();



                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);

                }

                //启动工厂方法

                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;

        }

        //当订阅关系发生变化省,更新订阅关系

        this.updateTopicSubscribeInfoWhenSubscriptionChanged();



        this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();

        //立即负载均衡

        this.mQClientFactory.rebalanceImmediately();

    }

工厂方法启动逻辑


public void start() throws MQClientException {



        synchronized (this) {

            switch (this.serviceState) {

                case CREATE_JUST:

                    this.serviceState = ServiceState.START_FAILED;

                    // If not specified,looking address from name server

                    if (null == this.clientConfig.getNamesrvAddr()) {

                        this.clientConfig.setNamesrvAddr(this.mQClientAPIImpl.fetchNameServerAddr());

                    }

                    // Start request-response channel

                    this.mQClientAPIImpl.start();

                    // Start various schedule tasks

                    this.startScheduledTask();

                    // Start pull service

                    this.pullMessageService.start();

                    // Start rebalance service

                    this.rebalanceService.start();

                    // Start push service

                    this.defaultMQProducer.getDefaultMQProducerImpl().start(false);

                    log.info("the client factory [{}] start OK", this.clientId);

                    this.serviceState = ServiceState.RUNNING;

                    break;

                case RUNNING:

                    break;

                case SHUTDOWN_ALREADY:

                    break;

                case START_FAILED:

                    throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null);

                default:

                    break;

            }

        }

    }

rebalanceService是一个线程任务类,在线程任务中定时factory执行调用负载均衡


    public void run() {

        log.info(this.getServiceName() + " service started");



        while (!this.isStoped()) {

            this.waitForRunning(WaitInterval);

            this.mqClientFactory.doRebalance();

        }



        log.info(this.getServiceName() + " service end");

    }

factory执行负载均衡,其实就是遍历factory中的所有consumer,调用doRebalance()方法


    public void doRebalance() {

        for (Map.Entry entry : this.consumerTable.entrySet()) {

            MQConsumerInner impl = entry.getValue();

            if (impl != null) {

                try {

                    impl.doRebalance();

                } catch (Exception e) {

                    log.error("doRebalance exception", e);

                }

            }

        }

    }

AllocateMessageQueueStrategy 队列分配策略

默认情况下PushConsumer的AllocateMessageQueueStrategy
    public DefaultMQPushConsumer() {

        this(MixAll.DEFAULT_CONSUMER_GROUP, null, new AllocateMessageQueueAveragely());

    }

你可能感兴趣的:(RocketMQ集群消费时队列分配)