RabbitTemplate.convertAndSend 有时发不出消息,是因为默认向空交换器(amq.direct)以队列名作路由键发送,要求队列已存在且绑定到该交换器,否则消息静默丢弃。

为什么 RabbitTemplate.convertAndSend 有时发不出消息?
不是代码写错了,而是默认没配交换器(exchange)和路由键(routingKey),直接往队列名发,属于“直连模式”的简化用法——但前提是该队列必须已存在、且已绑定到默认交换器 amq.direct 上,否则消息会静默丢弃。
- 默认行为:调用
RabbitTemplate.convertAndSend("notice_queue", message)实际等价于convertAndSend("", "notice_queue", message),即向空字符串交换器(默认直连交换器)、以队列名为路由键发送 - 隐患:如果队列是手动创建的但没绑定,或用了非默认 vhost,消息就卡在交换器里不落地
- 稳妥做法:显式声明交换器和绑定关系,哪怕只用一个
direct类型的自定义交换器,比如my_exchange,再通过@Bean配置Queue和Binding
@RabbitListener 不触发消费?先看这三件事
最常见原因不是注解没生效,而是监听器启动时队列还不存在,或者连接参数指向了错误的 vhost —— RabbitMQ 的虚拟主机是完全隔离的,/ 和 /test 是两个世界。
- 确认
queuesToDeclare = @Queue("notice_queue")中的队列名与生产者发的目标一致,大小写、下划线都不能错 - 检查
spring.rabbitmq.virtual-host配置是否匹配(默认是/,但很多团队会建/prod或/dev) - 留意 Spring Boot 启动日志里有没有类似
Auto-declaring a non-durable, auto-delete, exclusive queue的提示——这意味着它帮你临时建了个“一次性队列”,重启后就没了,没法被复用
发送端要不要设 deliveryMode = 2?
要,但得理解它真正控制的是什么:不是“消息一定不丢”,而是“Broker 收到后写磁盘”,前提是队列本身也声明为持久化(durable = true)。两者缺一不可。
-
RabbitTemplate默认不设deliveryMode,发出去的消息是非持久化的,Broker 内存 crash 就丢 - 正确姿势:在发送前设置属性,例如
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT) - 更省心的方式:统一在配置类里给
RabbitTemplate设默认属性,或用CorrelationData+ 确认回调做二次保障
消费者抛异常导致消息重复?别急着加 try-catch
默认情况下,RabbitMQ 在消费者处理失败(未 ack)后会把消息重新入队,可能立刻重试,也可能堆积等待。这不是 bug,是机制设计;但盲目捕获所有异常并吞掉,反而掩盖了真实问题。
- Spring 默认使用
SimpleMessageListenerContainer,失败后自动 nack 并 requeue - 想跳过某条坏数据?用
Channel.basicNack(deliveryTag, false, false)显式拒绝且不重入队 - 真要兜底容错:配合死信队列(DLX),让反复失败的消息进
dlq.notice_queue单独排查,而不是卡在线程池里反复 retry










