RocketMQ源码解析-topic创建机制

RocketMQ原理-自动创建Topic机制 背景 RocketMQ 源码版本 4.9.1-SNAPSHOT 下载了 RocketMQ 源码,在本地 IDE 中正常启动了 Broker 服务 和 NameServer 服务。 生产者发送消息的 demo 如下 在发送消息的过程中报错了,报错信息如下 从报错信息可以看到: **Topic 对应的路由信息没有正常获取到(Topic 名称为 TopicTest)。**也难怪,Broker 启动后没有人为地去创建 Topic 对应的队列,发送消息自然没办法获取到路由信息。 想到 Brok 阅读详情

以下源码基于Rocket MQ 4.7.0

1. RocketMQ Topic创建机制

RocketMQ Topic创建机制分为两种:一种自动创建,一种手动创建。可以通过设置broker的配置文件来禁用或者允许自动创建。默认是开启的允许自动创建

autoCreateTopicEnable=true/false

下面会结合源码来深度分析一下自动创建和手动创建的过程。

2. 自动Topic

默认情况下,topic不用手动创建,当producer进行消息发送时,会从nameserver拉取topic的路由信息,如果topic的路由信息不存在,那么会默认拉取broker启动时默认创建好名为“TBW102”的Topic,这定义在org.apache.rocketmq.common.MixAll类中

// Will be created at broker when isAutoCreateTopicEnable
 public static final String AUTO_CREATE_TOPIC_KEY_TOPIC = "TBW102"; 

自动创建开关是下BrokerConfig类中有一个私有变量:

@ImportantField
private boolean autoCreateTopicEnable = true;

这变量可以通过配置文件配置来进行修改,代码中的默认值为true,所以在默认的情况下Rocket MQ是会自动创建Topic的。
在Broker启动,会调用TopicConfigManager的构造方法,在构造方法中定义了一系列RocketMQ系统内置的一些系统Topic(这里只关注一下TBW102):

{
    // MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC
    if (this.brokerController.getBrokerConfig().isAutoCreateTopicEnable()) {
        String topic = MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC;
        TopicConfig topicConfig = new TopicConfig(topic);
        this.systemTopicList.add(topic);
        topicConfig.setReadQueueNums(this.brokerController.getBrokerConfig()
            .getDefaultTopicQueueNums()); //8
        topicConfig.setWriteQueueNums(this.brokerController.getBrokerConfig()
            .getDefaultTopicQueueNums()); //8
        int perm = PermName.PERM_INHERIT | PermName.PERM_READ | PermName.PERM_WRITE;
        topicConfig.setPerm(perm);
        this.topicConfigTable.put(topicConfig.getTopicName(), topicConfig);
    }
}

这里有 this.brokerController.getBrokerConfig().isAutoCreateTopicEnable() 这样一段代码,在开启允许自动创建的时候,会把当前Topic的信息存入topicConfigTable变量中。然后通过发送定期发送心跳包把Topic和Broker的信息发送到NameServer的RouteInfoManager中进行保存。在BrokerController中定义了这样的一个定时任务来执行这个心跳包的发送:

this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                try {
                    BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
                } catch (Throwable e) {
                    log.error("registerBrokerAll Exception", e);
                }
            }
        }, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);

这里就说明了如何把每个Broker的系统自定义的Topic注册到NameServer。接下来看在发送过程中如何从NameServer获取Topic的路由信息: DefaultMQProducerImpl.sendDefaultImpl

private SendResult sendDefaultImpl(
        Message msg,
        final CommunicationMode communicationMode,
        final SendCallback sendCallback,
        final long timeout
    ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {

        //省略代码

        //获取路由信息
        TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());

    }

通过DefaultMQProducerImpl.tryToFindTopicPublishInfo方法获取Topic的路由信息。

    private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) {

        TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic);
        //第一次从缓存中获取--肯定没有因为还没创建
        if (null == topicPublishInfo || !topicPublishInfo.ok()) {
            this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());
            //从NameServer获取--也是没有,因为没有创建
            this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
            topicPublishInfo = this.topicPublishInfoTable.get(topic);
        }

        if (topicPublishInfo.isHaveTopicRouterInfo() || topicPublishInfo.ok()) {
            return topicPublishInfo;
        } else {
            //第二次从这里获取
            this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer);
            topicPublishInfo = this.topicPublishInfoTable.get(topic);
            return topicPublishInfo;
        }
    }

下面来看一下 MQClientInstance.updateTopicRouteInfoFromNameServer 的方法:

 public boolean updateTopicRouteInfoFromNameServer(final String topic, boolean isDefault,
        DefaultMQProducer defaultMQProducer) {

    //省略代码

    if (isDefault && defaultMQProducer != null) {
            //使用默认的TBW102 Topic获取数据
            topicRouteData = this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(defaultMQProducer.getCreateTopicKey(),
                            1000 * 3);
                if (topicRouteData != null) {
                    for (QueueData data : topicRouteData.getQueueDatas()) {
                                int queueNums = Math.min(defaultMQProducer.getDefaultTopicQueueNums(), data.getReadQueueNums());
                                data.setReadQueueNums(queueNums);
                                data.setWriteQueueNums(queueNums);
                            }
                        }
                    } else {
                        //这是正常的
                        topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, 1000 * 3);
                    }
      //省略代码      
    }

如果isDefault=true并且defaultMQProducer不为空,从nameserver中获取默认路由信息,此时会获取所有已开启自动创建开关的broker的默认“TBW102”topic路由信息,并保存默认的topic消息队列数量。

这里会比较一下配在在 DefaultMQProducer.defaultTopicQueueNums中的默认值和TBW102中的值哪个更小。

 if (topicRouteData != null) {
        TopicRouteData old = this.topicRouteTable.get(topic);
        boolean changed = topicRouteDataIsChange(old, topicRouteData);
        if (!changed) {
            changed = this.isNeedUpdateTopicRouteInfo(topic);
        } else {
            log.info("the topic[{}] route info changed, old[{}] ,new[{}]", topic, old, topicRouteData);
        }
 }

判断获取默认的是否存在,如果存在把当前的Topic的信息更新。也就是把TBW102 Topic的数据更新为自动创建的数据。

 if (changed) {
    TopicRouteData cloneTopicRouteData = topicRouteData.cloneTopicRouteData();

    for (BrokerData bd : topicRouteData.getBrokerDatas()) {
        this.brokerAddrTable.put(bd.getBrokerName(), bd.getBrokerAddrs());
    }

    // Update Pub info
    {
        TopicPublishInfo publishInfo = topicRouteData2TopicPublishInfo(topic, topicRouteData);
        publishInfo.setHaveTopicRouterInfo(true);
        Iterator<Entry<String, MQProducerInner>> it = this.producerTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, MQProducerInner> entry = it.next();
            MQProducerInner impl = entry.getValue();
            if (impl != null) {
                impl.updateTopicPublishInfo(topic, publishInfo);
            }
        }
    }
        // Update sub info
    {
        Set<MessageQueue> subscribeInfo = topicRouteData2TopicSubscribeInfo(topic, topicRouteData);
        Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, MQConsumerInner> entry = it.next();
            MQConsumerInner impl = entry.getValue();
            if (impl != null) {
                impl.updateTopicSubscribeInfo(topic, subscribeInfo);
            }
        }
    }
    log.info("topicRouteTable.put. Topic = {}, TopicRouteData[{}]", topic, cloneTopicRouteData);
    this.topicRouteTable.put(topic, cloneTopicRouteData);
    return true;
}

更新本地的缓存。这样TBW102 Topic的负载和一些默认的路由信息就会被自己创建的Topic使用。这里就是整个自动创建的过程.
总结一下就是:通过使用系统内部的一个TBW102的Topic的配置来自动创建当前用户的要创建的自定义Topic。

3. 手动创建--预先创建

手动创建也叫预先创建,就是在使用Topic之前就创建,可以通过命令行或者通过RocketMQ的管理界面创建Topic。

通过界面控制台创建

项目地址: github.com/apache/rock…

TopicController主要负责Topic的管理

@RequestMapping(value = "/createOrUpdate.do", method = { RequestMethod.POST})
@ResponseBody
public Object topicCreateOrUpdateRequest(@RequestBody TopicConfigInfo topicCreateOrUpdateRequest) {
    Preconditions.checkArgument(CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getBrokerNameList()) || CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getClusterNameList()),
            "clusterName or brokerName can not be all blank");
    logger.info("op=look topicCreateOrUpdateRequest={}", JsonUtil.obj2String(topicCreateOrUpdateRequest));
    topicService.createOrUpdate(topicCreateOrUpdateRequest);
    return true;
}

然后通过MQAdminExtImpl.createAndUpdateTopicConfig方法来创建:

    @Override
    public void createAndUpdateTopicConfig(String addr, TopicConfig config)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        MQAdminInstance.threadLocalMQAdminExt().createAndUpdateTopicConfig(addr, config);
    }

通过调用DefaultMQAdminExtImpl.createAndUpdateTopicConfig创建Topic

@Override
public void createAndUpdateTopicConfig(String addr, TopicConfig config) throws RemotingException, MQBrokerException,
        InterruptedException, MQClientException {
    this.mqClientInstance.getMQClientAPIImpl().createTopic(addr, this.defaultMQAdminExt.getCreateTopicKey(), config, timeoutMillis);
}

最后通过MQClientAPIImpl.createTopic创建Topic

    public void createTopic(final String addr, final String defaultTopic, final TopicConfig topicConfig,
        final long timeoutMillis)
        throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
        CreateTopicRequestHeader requestHeader = new CreateTopicRequestHeader();
        requestHeader.setTopic(topicConfig.getTopicName());
        requestHeader.setDefaultTopic(defaultTopic);
        requestHeader.setReadQueueNums(topicConfig.getReadQueueNums());
        requestHeader.setWriteQueueNums(topicConfig.getWriteQueueNums());
        requestHeader.setPerm(topicConfig.getPerm());
        requestHeader.setTopicFilterType(topicConfig.getTopicFilterType().name());
        requestHeader.setTopicSysFlag(topicConfig.getTopicSysFlag());
        requestHeader.setOrder(topicConfig.isOrder());

        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UPDATE_AND_CREATE_TOPIC, requestHeader);

        RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr),
            request, timeoutMillis);
        assert response != null;
        switch (response.getCode()) {
            case ResponseCode.SUCCESS: {
                return;
            }
            default:
                break;
        }

        throw new MQClientException(response.getCode(), response.getRemark());
    }

 

RocketMQ自动创建Topic机制:原理、配置与生产环境实践 在分布式消息队列系统中,Topic作为消息的类别标识,是生产者与消费者通信的基础单元。其核心原理在于通过命名服务进行路由注册与发现,确保消息能准确投递到指定队列。这一机制的技术价值在于简化了系统部署与运维流程,尤其在开发测试阶段,能够避免因资源未就绪导致的流程阻塞。在实际应用场景中,自动创建Topic功能通过预设模板Topic(如TBW102)和配置开关(autoCreateTopicEnable),实现了消息发送时的按需资源创建。然而,这一便利性也带来了队列数配置、负载均衡等潜在问题,需要结合具体业务需求 阅读详情

相关推荐

RocketMQ 消息路由解析——图解、源码级解析

???? Java学习:Java从入门到精通总结 ???? Spring系列推荐:Spring源码解析 ???? 最近更新:2022年1月26日 ???? 个人简介:通信工程本硕????、代码界新人????。我的故事充满机遇、挑战与翻盘,欢迎关注作者来共饮一杯鸡汤 ???? 点赞 ???? 收藏 ⭐留言 ???? 都是我最大的动力! 文章目录消息路由获取Topic获取路由信息函数tryToFindTopicPublishInfoTopic 路由信息表合并路由数据 消息路由 在RocketMQ的系

如切如磋,如琢如磨 2637

RocketMQ中的autoCreateTopicEnable是如何实现自动创建topic?

我们知道在rocketmq的broker中可以设置autoCreateTopicEnable,当autoCreateTopicEnable = true的时候,如果生产者往broker中发送消息并指定了一个broker中不存在的topic,那么也是可以发送成功的,因为broker会自动地去创建这个不存在的topic,下面我们就来看一下rocketmq中是如何实现的 一.生产者发送消息 问题引出:在生产者发送消息之后,首先它需要知道消息要发送的broker地址,而broker的地址属于路由信息的一部分.

我神级欧文的博客 1万+

Ubuntu20.04运行Vins-fusion

因已安装ROS noetic,eigen,opencv及ceres等库,这部分环境配置就不细讲,直接下载VINS-FUSION的代码库 下载编译 cd ~/vins-fusion/src git clone https://github.com/HKUST-Aerial-Robotics/VINS-Fusion.git cd ../ catkin_make source ~/catkin_ws/devel/setup.bash 编译时会发现一堆错误,别急,无非就是环境冲突问题,一个个解决 1)首先,ROS

xiaojinger_123的博客 1万+

RocketMQ实战:生产环境中,autoCreateTopicEnable为什么不能设置为true

很多网友会问,为什么明明集群中有多台Broker服务器,autoCreateTopicEnable设置为true,表示开启Topic自动创建,但新创建的Topic的路由信息只包含在其中一台Broker服务器上,这是为什么呢? 期望值:为了消息发送的高可用,希望新创建的Topic在集群中的每台Broker上创建对应的队列,避免Broker的单节点故障。 现象截图如下: 正如上图所示,自动创建的to...

qq_41490913的博客 568

数字孪生实战:如何用Blender+Unity快速搭建未来工厂3D模型(附资源包)

本文提供了一套利用Blender进行三维建模,再结合Unity构建虚拟仿真环境的实用方案,旨在帮助中小型制造企业低成本、高效率地搭建未来工厂数字孪生原型系统。文章详细介绍了从数据规划、模型优化到场景构建、逻辑仿真与数据对接的全流程,并附有资源包,助力技术团队快速实现从静态展示到动态交互的数字孪生应用。

weixin_29191669的博客 338

手把手教程:使用C#实现工业上位机开发

深入讲解如何利用C#进行上位机开发,涵盖通信协议与界面设计,助力掌握工业自动化核心技能。结合实际案例,让上位机开发更高效直观。

weixin_32480007的博客 2065

RocketMQ 事务消息原理详解 & 源码解析

*** 处理事务半消息(回查其执行状态)* @param msgExt 事务半消息*/= null) {@Overridetry {", e);});} else {/*** 发送回查请求到生产者客户端* @param msgExt 事务半消息*/// 从消息属性中复原真实的 topic 和 queueId// 轮询选择一个可用的生产者客户端通道= null) {// 发送回查请求到生产者客户端,回查事务消息执行状态。

金甲虫的博客 1173

SQL Server 内存占用高分析及解决办法(超详细)

如果想让 Sql Server 主动释放 占用并空闲的内存空间,可以设置Sql Server占用内存的上限,就会让Sql server在。如执行存储过程,自定函数时,SQL Server 需要先二进制编译再运行,编译后的结果也会缓存起来,再次调用时就无需再次编译。才会释放一点占用的内存,所以很多时候,我们会发现运行SQL Server的系统内存往往居高不下这些内存一般都。缓存,以便于再次请求此页的数据的时候,直接从内存返回,就无需读取磁盘了,大大提高了速度。最后文章有啥不对,欢迎大佬在评论区指点!

2301_79080944的博客 3952

深度解析RocketMQ Topic的创建机制

我还记得第一次使用rocketmq的时候,需要去控制台预先创建topic,我当时就想为什么要这么设计,于是我决定撸一波源码,带大家从根源上吃透rocketmq topic...

后端进阶 2万+

利用OTSU阈值算法在ZYNQ上实现浒苔识别

利用OTSU阈值算法进行图像二值化,结果用HDMI显示

qq_38075564的博客 1275

Nmap的介绍、安装 并进行网络扫描

Nmap概述 Nmap(Network Mapper(网络映射器))是一个网络连接端扫描软件,用来扫描网上电脑开放的网络连接端。确定哪些服务运行在哪些连接端,并且推断计算机运行哪个操作系统(这是亦称 fingerprinting)。它是网络管理员必用的软件之一,以及用以评估网络系统安全。 正如大多数被用于网络安全的工具,nmap 也是不少黑客及骇客(又称脚本小子)爱用的工具 。系统管理员可以利用nmap来探测工作环境中未经批准使用的服务器,但是黑客会利用nmap来搜集目标电脑的网络设定,从而计划攻击的方法。

Cheney ' blog 2万+

RocketMQ源码 创建Topic流程源码分析

MQAdminImpl MQ管理组件提供了大量对mq进行管理的工具,其中一个就是创建Topic。它内部实现是通过 mqClient工具从 NameServer拉取当前 Topic对应的路由元数据信息,解析遍历和当前topic有关的 broker高可用分组集合,找到分组中的 master主节点,然后依次对 broker主节点发送创建topic请求。broker 高可用分组的主节点收到请求后,将 topic元数据存储在 TopicConfigManager元数据管理组件中,然后上报当前 broker节点 Top

hzwangmr的博客 1902

全网首发--Unet3+改进16:在不同位置添加RepLKBlock超大卷积核

本文提出了一种基于大核卷积的神经网络架构RepLKNet,旨在缩小CNN与ViTs之间的性能差距。通过重新审视CNN中的大核设计,研究证明使用少量大卷积核替代多个小核能获得更强大的性能。提出的RepLKNet采用31×31大卷积核,在ImageNet和下游任务上取得了与SwinTransformer相当或更好的结果,同时具有更低延迟。文章详细介绍了实现步骤:(1)创建RepLKBlock模块文件;(2)在模型层中添加注意力机制;(3)在UNet模型中集成RepLKBlock;(4)完成训练流程。该方法展现了

AIcurator的博客 621

【RocketMQ进阶二】深度解析RocketMQ 主题的创建机制,为何生产建议关掉自动创建Topic

我还记得第一次使用rocketmq的时候,需要去控制台预先创建topic,我当时就想为什么要这么设计,于是我决定撸一波源码,带大家从根源上吃透rocketmq topic的创建机制。 topic在rocketmq的设计思想里,是作为同一个业务逻辑消息的组织形式,它仅仅是一个逻辑上的概念,而在一个topic下又包含若干个逻辑队列,即消息队列,消息内容实际是存放在队列中,而队列又存储在broker中,下面我用一张图来说明topic的存储模型: 其实rocketmq中存在两种不同的topic创建方式,.

骏马逸动,心随你动的博客 3071

目标跟踪与视频时序分析基础

两帧均有目标,却因快速移动导致检测框无重叠,仅靠交并比(IoU)无法维持身份关联。本文通过手算与纯Python实现最小跟踪流程,揭示IoU仅为位置线索,不能应对快速位移或遮挡。实验表明,即使目标真实存在,低重叠也会触发新编号。强调跟踪需结合运动预测、外观特征与阈值验证,避免误判。

Mr数据杨 2335

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

深度解析 RocketMQ Topic 的创建机制

从用户的视角来感受一个开源项目的成长,是我们推出「开发者说」专栏的初衷,即在开发者进行开源项目选型时,提供更为立体的项目信息。本文是「开发者说」的第7篇,作者张乘辉,Gi...

阿里巴巴中间件 447

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

RocketMQ Topic创建【源码笔记】

1.Topic创建的方式Topic的创建分为自动创建和通过命令行创建两种。通过broker配置参数autoCreateTopicEnable设置。 通常可以在非生产环境开启自动创建,生产...

gaoliang1719的专栏 901

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

上一篇: 关于json序列化
下一篇: 项目一共30个模块,你叫我maven版本一个个手动改?
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值