spring中使用rabbitmq延迟插件踩坑

问题描述

使用 rabbitmq_delayed_message_exchange 插件时,ReturnCallback 会被强制触发,错误码302,错误信息为NO_ROUTE。

问题复现

spring配置如下

spring.rabbitmq.host=
spring.rabbitmq.port=
spring.rabbitmq.virtual-host=
spring.rabbitmq.username=
spring.rabbitmq.password=
# 下面两个配置都可以
spring.rabbitmq.template.mandatory=true
spring.rabbitmq.publisher-returns=true

配置ReturnCallback的代码如下

package com.imooc.mq.config;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class RabbitTemplateConfig implements InitializingBean {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void afterPropertiesSet() throws Exception {
        rabbitTemplate.setReturnsCallback(returned -> {
            try {
                String exchange = returned.getExchange();
                String routingKey = returned.getRoutingKey();
                Message amqpMessage = returned.getMessage();
                Object msgBody = rabbitTemplate.getMessageConverter().fromMessage(amqpMessage);
                String error = returned.getReplyCode() + " " + returned.getReplyText();
                log.error("MQ消息发送失败 returnCallback exchange: {}, routingKey: {}, msgBody: {}, error: {}", exchange, routingKey, JSON.toJSONString(msgBody), error);
            } catch (Exception e) {
                log.error("MQ消息发送失败", e);
            }
        });
    }
}

配置延迟插件的交换机代码如下

@Bean
public CustomExchange retryExchange() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-delayed-type", "direct"); // 底层路由模式
    return new CustomExchange("xxx",
            "x-delayed-message", // 固定类型
            true,  // 持久化
            false, // 不自动删除
            args
    );
}

这种情况下,发送延迟消息,rabbitmq都会回调return异常处理器,我们就没办法和正常的错误(未配置队列)区分开了。

问题原因

延迟插件的核心逻辑是:

  • 消息先被暂存在交换机中,不会立即路由到队列;
  • 等延迟时间到期后,插件才会将消息投递到目标队列。

因此,在消息首次发送到交换机时,队列是不可达的,Spring AMQP 会触发 ReturnCallback 通知 “消息未被路由”。这不是 bug,而是插件机制本身的设计。

解决方法

  1. 关闭 return 回调(全局配置)
spring.rabbitmq.template.mandatory=true
spring.rabbitmq.publisher-returns=true

去除这两个配置,或者配置为false,并去除returnCallback
2. 忽略延迟交换机
在发送延迟消息时,消息头增加指定属性,在returnCallback处理时忽略包含属性的消息

posted @ 2025-12-29 22:06  strongmore  阅读(4)  评论(0)    收藏  举报