中间件

RabbitMQ 发消息报错 Listener not registered

言七墨 · 11月29日 · 2019年 · · · 3341次已读

遇到问题

在完成 MQ 的发送方代码后,通过单元测试(mock 方式)发现发送方代码是没有问题的,但是通过 JOB 发送消息就一直提示“Listener not registered..”,又通过 http 直接调用接口来发送消息,也是提示“Listener not registered..”,然后各种检查配置、各种谷歌都无济于事,最后只能 debug 源码了。

下面是主要报错信息:

Caused by: java.lang.IllegalArgumentException: Listener not registered: org.springframework.amqp.rabbit.core.RabbitTemplate@2d5e608a[org.springframework.amqp.rabbit.core.RabbitTemplate@3d1c806] at org.springframework.util.Assert.notNull(Assert.java: 134) at org.springframework.amqp.rabbit.support.PublisherCallbackChannelImpl.addPendingConfirm(PublisherCallbackChannelImpl.java: 778) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java: 62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java: 43) at java.lang.reflect.Method.invoke(Method.java: 498) at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$CachedChannelInvocationHandler.invoke(CachingConnectionFactory.java: 980) at com.sun.proxy.$Proxy126.addPendingConfirm(Unknown Source) at org.springframework.amqp.rabbit.core.RabbitTemplate.setupConfirm(RabbitTemplate.java: 1552) at org.springframework.amqp.rabbit.core.RabbitTemplate.doSend(RabbitTemplate.java: 1529) at org.springframework.amqp.rabbit.core.RabbitTemplate$3.doInRabbit(RabbitTemplate.java: 716) at org.springframework.amqp.rabbit.core.RabbitTemplate.doExecute(RabbitTemplate.java: 1461)...116 common frames omitted

debug 解决

在经过长时间的debug后,才发现原来是“org.springframework.boot:spring-boot-starter-actuator”导致的,经查,这个 jar 包是对项目进行健康监控的,它随项目的启动而启动。首先 RabbitMQ 通过我们自己的配置在项目启动的时候,会通过 @Bean 注解的方式将 SimpleRoutingConnectionFactory 强转为 CachingConnectionFactory 交由 Spring 管理。下面是 RabbitMQ 的配置代码:

@Bean("sessionProducerRabbitAdmin")
public RabbitAdmin sessionProducerRabbitAdmin(RabbitMessagingTemplate rabbitMessagingTemplate) {
        RabbitTemplate rabbitTemplate = rabbitMessagingTemplate.getRabbitTemplate();
        rabbitTemplate.setReturnCallback(messageCheckCallBack);
        rabbitTemplate.setConfirmCallback(messageCheckCallBack);
        SimpleRoutingConnectionFactory simpleRoutingConnectionFactory =
                (SimpleRoutingConnectionFactory) rabbitTemplate.getConnectionFactory();
        CachingConnectionFactory connectionFactory =
                (CachingConnectionFactory) simpleRoutingConnectionFactory.getTargetConnectionFactory("message");
        rabbitTemplate.setMandatory(connectionFactory.isPublisherReturns());
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        rabbitAdmin.declareExchange(new TopicExchange(MQ_EXCHANGE));
        return rabbitAdmin;
}

后面 actuator 这个 jar 包开始执行,它会对 RabbitMQ 进行健康检查,在检查的时候,它又单独去创建了一个连接,并返回了一个新的工厂 SimpleRoutingConnectionFactory,由于 SimpleRoutingConnectionFactory 不是 PublisherCallbackChannelConnectionFactory 的实例,就没有将 Channel(Listener) 塞给 publisherConfirmChannels ,导致当系统通过 confirm 方式(confirm 方式:保证消息的可靠性)保证消息可靠性时,由于拿不到 Listener,就抛出了“Listener not registered..”异常。

解决办法

单独去掉 RabbitMQ 的健康检查或者直接去掉这个 jar 包的引入(最后确定这个 jar 的引入对于目前项目来说没有什么用处)。哪位大佬遇到过此问题,有没有更好的解决办法呢?

1 条回应
  1. 浮生若梦2019-12-17 · 18:43

    It’s a bug with the new DirectReplyToContainer mechanism for handling replies.

    Set template.setUseDirectReplyToContainer(false); as a work around (reverts to the previous mechanism).