解决多个rocketmq 多个MQ实例时使用DefaultMQProducer NamesrvAddr被覆盖问题

解决多个rocketmq 多个MQ实例时使用DefaultMQProducer NamesrvAddr被覆盖问题

背景:系统里已经有一个mq在使用,现在要接入其他mq,实现和其他系统的消息对接,所以两个mq有自己独立的配置,要相互不影响。使用DefaultMQProducer创建之后发现setNamesrvAddr配置参数成功,但是获取到的brokerName全是loaclhost,也就是说获取到的全是原来系统在使用的mq的broker。

翻看源码发现DefaultMQProducerImpl.java 的169 170行存在问题,在此之前this.defaultMQProducer的NamesrvAddr是新的配置ip,但是获取到的mQClientFactory不正确,继续往下看getOrCreateMQClientInstance方法

if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
                    this.defaultMQProducer.changeInstanceNameToPID();
                }

                this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook);

                boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this);

通过getOrCreateMQClientInstance方法可以看到MQClientInstance是从 factoryTable这个Map中获取的,如果根据key值clientId获取不到的话才会创建新的MQClientInstance,点进clientConfig.buildMQClientId()方法

    public MQClientInstance getOrCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) {
        String clientId = clientConfig.buildMQClientId();
        MQClientInstance instance = this.factoryTable.get(clientId);
        if (null == instance) {
            instance =
                new MQClientInstance(clientConfig.cloneClientConfig(),
                    this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook);
            MQClientInstance prev = this.factoryTable.putIfAbsent(clientId, instance);
            if (prev != null) {
                instance = prev;
                log.warn("Returned Previous MQClientInstance for clientId:[{}]", clientId);
            } else {
                log.info("Created new MQClientInstance for clientId:[{}]", clientId);
            }
        }

        return instance;
    }

可以看到clientId的生成规则是ip+@InstanceName,其中InstanceName每次启动生成的就是固定的,ip是启动的时候本身的ip,所以每次启动生成的clientId都是一致的,而在我的代码使用DefaultMQProducer创建实例的时候就已经初始化过了,factoryTable中已经存在老mq的实例,所以新mq通过一模一样的clientId只能获取到老mq的配置生成的MQClientInstance

    public String buildMQClientId() {
        StringBuilder sb = new StringBuilder();
        sb.append(this.getClientIP());

        sb.append("@");
        sb.append(this.getInstanceName());
        if (!UtilAll.isBlank(this.unitName)) {
            sb.append("@");
            sb.append(this.unitName);
        }

        return sb.toString();
    }

看到代码里生成 clientId时还加上了unitName,其实这里如果 unitName不同,那么生成的 clientId就不同了。在DefaultMQProducer实例化的时候调用setUnitName即可

DefaultMQProducer defaultMQProducer = new DefaultMQProducer("group");
defaultMQProducer.setUnitName("yourName");

或者升级RocketMQ Client到4.9.0 ,我用的rocketmq-spring-boot-starter升级到2.3.0也行

升级后的instanceName会加毫秒值

public void changeInstanceNameToPID() {
    if (this.instanceName.equals("DEFAULT")) {
        this.instanceName = UtilAll.getPid() + "#" + System.nanoTime();
    }
}

buildMQClientId也有所改动

public String buildMQClientId() {
    StringBuilder sb = new StringBuilder();
    sb.append(this.getClientIP());
    sb.append("@");
    sb.append(this.getInstanceName());
    if (!UtilAll.isBlank(this.unitName)) {
        sb.append("@");
        sb.append(this.unitName);
    }

    if (this.enableStreamRequestType) {
        sb.append("@");
        sb.append(RequestType.STREAM);
    }

    return sb.toString();
}
### RocketMQ 基本用法与简单入门示例 #### 下载与安装 为了开始使用 RocketMQ,首先需要从官方提供的下载地址获取最新版本的软件包。可以通过访问以下链接完成下载操作[^1]: - 官方下载页面: [http://rocketmq.apache.org/release_notes/release-notes-4.2.0/] 解压后会得到一个包含多个目录和文件的压缩包。 --- #### 启动 NameServer 和 Broker 在运行任何消息传递之前,必须先启动 **Name Server** 和 **Broker** 服务: 1. **启动 NameServer** 执行以下命令来启动 NameServer 实例: ```bash nohup sh bin/mqnamesrv & ``` 2. **启动 Broker** 接下来,通过指定 `NAMESRV_ADDR` 参数启动 Broker 节点: ```bash export NAMESRV_ADDR=localhost:9876 nohup sh bin/mqbroker -n localhost:9876 & ``` 上述两步完成后,可以验证服务状态是否正常运行: ```bash sh bin/mqadmin clusterList -n localhost:9876 ``` 如果一切配置无误,则可以看到集群列表的相关信息。 --- #### 配置并启动管理控制台 (可选) 对于更直观的消息管理和监控功能,可以选择部署 RocketMQ 的 Web 控制台工具。按照如下方式执行即可[^2]: ```bash cd /path/to/rocketmq-console/target/ java -jar rocketmq-console-ng-1.0.1.jar \ --server.port=9877 \ --rocketmq.config.namesrvAddr=127.0.0.1:9876 ``` 此可通过浏览器访问 http://localhost:9877 来查看实数据流情况。 --- #### 编写生产者与消费者代码 以下是基于 Java SDK 创建简单的消息生产和消费逻辑的例子。 ##### 生产者示例 下面是一个基本的 Producer 示例程序,用于向队列发送一条测试消息: ```java import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.common.message.Message; public class Producer { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("test_group"); producer.setNamesrvAddr("127.0.0.1:9876"); producer.start(); Message msg = new Message( "TopicTest", // 主题名称 "TagA", // 消息标签 ("Hello RocketMQ").getBytes() // 消息体内容 ); producer.send(msg); System.out.println("Message sent successfully."); producer.shutdown(); } } ``` ##### 消费者示例 对应的 Consumer 则负责接收来自主题中的消息,并打印其具体内容到终端屏幕上: ```java import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.common.message.MessageExt; import java.util.List; public class Consumer { public static void main(String[] args) throws Exception { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test_group"); consumer.setNamesrvAddr("127.0.0.1:9876"); consumer.subscribe("TopicTest", "*"); consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { for (MessageExt msg : msgs) { System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), new String(msg.getBody())); } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("Consumer started..."); } } ``` 以上即完成了整个流程设置——从环境搭建至实际编码实现部分均有所涉及。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张宜强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值