org.apache.rocketmq.client.exception.MQClientException: No route info of this topic

小编:管理员 765阅读 2022.08.03

笔者最近在使用rocketmq时,使用部署在docker中的rocketmq就会报“org.apache.rocketmq.client.exception.MQClientException: No route info of this topic”异常,win10本地启动同版本的rocketmq release库没有任何问题,搜索了许久仍然没有找到问题,关于这个问题,部分原因在笔者,笔者没有完全按照http://rocketmq.apache.org/docs/simple-example/里面的demo引入下面的rocketmq-client依赖,而是引入的rocketmq-spring-boot-starter这个依赖,示例代码如下:

@Test
public  void send() throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {
    //Instantiate with a producer group name.
    DefaultMQProducer producer = new
            DefaultMQProducer("my-group1");
    //rocketmq nameserver地址
    producer.setNamesrvAddr("172.21.52.27:9876");
    producer.start();
    Message msg = new Message("test" /* Topic */,
            "TagA" /* Tag */,
            ("Hello RocketMQ ").getBytes("utf-8") /* Message body */
    );
    //Call send message to deliver message to one of brokers.
    SendResult sendResult = producer.send(msg);
    System.out.printf("%s%n", sendResult);
    //Shut down once the producer instance is not longer in use.
    producer.shutdown();
}
复制

经过跟踪发现标红部分的代码根本没有起作用,为什么没起作用呢,下面看下DefaultMQProducer的构造方法:

public DefaultMQProducer(String producerGroup, RPCHook rpcHook) {
        this.log = ClientLogger.getLog();
        this.createTopicKey = "TBW102";
        this.defaultTopicQueueNums = 4;
        this.sendMsgTimeout = 3000;
        this.compressMsgBodyOverHowmuch = 4096;
        this.retryTimesWhenSendFailed = 2;
        this.retryTimesWhenSendAsyncFailed = 2;
        this.retryAnotherBrokerWhenNotStoreOK = false;
        this.maxMessageSize = 4194304;
        this.traceDispatcher = null;
        this.producerGroup = producerGroup;
        //创建DefaultMQProducerImpl对象
        this.defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
    }


    //发送消息
    public SendResult send(Message msg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
        return this.defaultMQProducerImpl.send(msg);
    }
复制

从send方法以及DefaultMQProducer的构造方法可以看出,引入rocketmq-spring-boot-starter引入后导致MQClientInstance实例被提前创建,提前创建使用的是application.yml文件中配置:

rocketmq:
  name-server: 127.0.0.1:9876
  producer:
    group: my-group
复制

该配置里面使用的就是127.0.0.1 而不是docker中部署的nameserver地址,虽然使用producer.setNamesrvAddr("172.21.52.27:9876")设置了rocketmq nameserver地址,但是其实是无效的,具体原因可以在MQClientManager类getAndCreateMQClientInstance方法查看,

public MQClientInstance getAndCreateMQClientInstance(ClientConfig clientConfig, RPCHook rpcHook) {
    String clientId = clientConfig.buildMQClientId();
    MQClientInstance instance = (MQClientInstance)this.factoryTable.get(clientId);
    if (null == instance) {
        instance = new MQClientInstance(clientConfig.cloneClientConfig(), this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook);
        MQClientInstance prev = (MQClientInstance)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;
}
复制

由于引入rocketmq-spring-boot-starter导致getAndCreateMQClientInstance过早被创建,所以后续同一clientId不再创建MQClientInstance而使用最初创建的实例!

产生问题的原因在于最初application.proerties配置的是127.0.0.1:9876 虽然application.yml配对了地址,但是由于properties文件优先级比yaml文件高,导致一直使用的是127.0.0.1:9876这个错误地址,但是本地没有开启rocketmq服务,所以一直报"org.apache.rocketmq.client.exception.MQClientException: No route info of this topic"这个异常,搜索了很多最终都没有解决问题,最终单步调试解决了该问题。

PS:

1、是rocketmq的错误提示信息不够细,如果报connection exception之类的异常,也许早就解决了这个问题,但是却报出了很多场景都可能出现的”org.apache.rocketmq.client.exception.MQClientException: No route info of this topic“这个错误

2、引入rocketmq-spring-boot-starter以来时不要使用DefaultMQProducer发送信息

关联标签: