RocketMQ-生产者和消费者如何获取最新的Topic信息

MQ系统中,生产者和消费者需要获取Topic的最新队列信息以进行消息发送和消费。NameServer保存所有topic信息,Broker通过心跳包定时上报。生产者与消费者会主动或由定时任务触发更新Topic路由信息,确保从NameServer获取最新数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码版本号:版本号:4.9.4

生产者发送消息时,需要拿到topic的所有队列,从队列列表里面选择一个队列进行发送。
消息队列负载和重新分布时需要拿到topic的所有队列,然后计算出自己应该消费哪些队列。
所以生产者和消费者需要知道最新的topic有哪些队列。

对于生产者,发送消息时,会先从DefaultMQProducerImpl中的topicPublishInfoTable属性中获取,
如果topicPublishInfoTable属性中不存在,则会调用
MQClientInstance.updateTopicRouteInfoFromNameServer(java.lang.String)
NameServer获取最新的topic信息。

对于消费者,启动后就会调用DefaultMQPushConsumerImpl#updateTopicSubscribeInfoWhenSubscriptionChanged方法,
遍历自己订阅的topic信息,
然后调用MQClientInstance#updateTopicRouteInfoFromNameServer(java.lang.String)
NameServer获取最新的topic信息。

生产者和消费者都会主动触发updateTopicRouteInfoFromNameServer来获取最新的topic信息,除了主动触发,还会有定时任务触发。

MQClientInstance启动的定时任务

public class MQClientInstance {
    // 256行
    private void startScheduledTask() {
        // 271行 每隔30s执行一次
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    MQClientInstance.this.updateTopicRouteInfoFromNameServer();
                } catch (Exception e) {
                    log.error("ScheduledTask updateTopicRouteInfoFromNameServer exception", e);
                }
            }
        }, 10, this.clientConfig.getPollNameServerInterval(), TimeUnit.MILLISECONDS);
    }
}

updateTopicRouteInfoFromNameServer会遍历所有消费者订阅的topic和
所有生产者DefaultMQProducerImpl中的topicPublishInfoTable保存的topic,
调用MQClientInstance#updateTopicRouteInfoFromNameServer(java.lang.String)
NameServer获取最新的topic信息。

public class MQClientInstance {
    private final static long LOCK_TIMEOUT_MILLIS = 3000;
    private final Lock lockNamesrv = new ReentrantLock();
    /**
     * 用来保存topic的路由信息, 从nameserver获取来的信息直接保存到这里
     * TopicRouteData包含
     * QueueData列表(brokerName、readQueueNums、writeQueueNums)
     * BrokerData列表(cluster、brokerName、brokerAddrs(key=brokerId, value=brokerId的地址))
     */
    private final ConcurrentMap<String/* Topic */, TopicRouteData> topicRouteTable = new ConcurrentHashMap<String, TopicRouteData>();

    /**
     * 保存brokerName、brokerId、brokerAddress
     */
    private final ConcurrentMap<String/* Broker Name */, HashMap<Long/* brokerId */, String/* address */>> brokerAddrTable =
            new ConcurrentHashMap<String, HashMap<Long, String>>();
    
    /**
     * 版本号:4.9.4
     * 找到509行
     * MQClientInstance启动的时候会生成一个定时任务
     * 每隔30s执行一次(获取所有生产者和消费者里面的topic集合, 然后分别调用该方法)
     */
    public boolean updateTopicRouteInfoFromNameServer(final String topic) {
        return updateTopicRouteInfoFromNameServer(topic, false, null);
    }
    /**
     * 找到606行
     * @param isDefault
     * 如果为false, 如果这个topic还没有创建, 那么nameserver是查询不到的
     * 如果为true, 则获取默认的topic:TBW102(TopicValidator.AUTO_CREATE_TOPIC_KEY_TOPIC)
     */
    public boolean updateTopicRouteInfoFromNameServer(final String topic, boolean isDefault,
                                                      DefaultMQProducer defaultMQProducer) {
        try {
            // 加锁
            if (this.lockNamesrv.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
                try {
                    TopicRouteData topicRouteData;
                    if (isDefault && defaultMQProducer != null) {
                        /**
                         * 查询默认的topic信息, 可以发现并没有传该方法的topic字段
                         * 查询默认的topic主要是为了拿到默认topic的读写队列个数, 这样生产者才能选择队列然后进行发送消息
                         * 在broker保存消息的时候, 如果topic不存在, 如果允许自动创建topic, 此时才会在broker生成topic信息
                         */
                        topicRouteData = this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(defaultMQProducer.getCreateTopicKey(),
                                clientConfig.getMqClientApiTimeout());
                        if (topicRouteData != null) {
                            for (QueueData data : topicRouteData.getQueueDatas()) {
                                int queueNums = Math.min(defaultMQProducer.getDefaultTopicQueueNums(), data.getReadQueueNums());
                                data.setReadQueueNums(queueNums);
                                data.setWriteQueueNums(queueNums);
                            }
                        }
                    } else {
                        // 如果查询不到对应的topic信息
                        topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, clientConfig.getMqClientApiTimeout());
                    }
                    if (topicRouteData != null) {
                        /**
                         * 省略
                         * 1.更新brokerAddrTable
                         * 2.遍历producerTable 更新每个DefaultMQProducerImpl中的topicPublishInfoTable
                         *  这样生产者就知道每个topic都有哪些队列
                         * 3.遍历consumerTable 更新每个消费者中的topicSubscribeInfoTable(在RebalanceImpl中)
                         *  这样消费者就知道每个topic都有哪些队列, 然后根据规则计算出自己需要拉取哪些队列的消息
                         * 4.更新topicRouteTable
                         */
                        return true;
                    }
                } catch (Exception e) {
                    // 省略掉异常处理
                } finally {
                    // 解锁
                    this.lockNamesrv.unlock();
                }
            }
        } catch (InterruptedException e) {
            log.warn("updateTopicRouteInfoFromNameServer Exception", e);
        }
        return false;
    }
}

为什么NameServer会有所有的topic信息,topic信息不是保存在Broker吗?
Broker启动后,会有定时任务每隔30sNameServer发送心跳信息,这些心跳信息就包括所有的topic信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值