南昌市网站建设_网站建设公司_响应式网站_seo优化
2026/1/13 9:54:08 网站建设 项目流程
    • 先搞懂:啥是“无法路由的消息”?
    • 无法路由消息的3个“归宿”:看配置决定命运
      • 1. 默认情况:直接丢弃(最容易踩坑)
      • 2. mandatory=true:退回给生产者
        • 第一步:生产者发送时设置 mandatory=true
        • 第二步:配置退回回调,接收退回的消息
      • 3. 配置 AE 交换机:转发到“备胎”交换机
        • 第一步:配置 AE 交换机和对应的队列
        • 第二步:原交换机指定 AE 交换机
        • 第三步:生产者发送消息
    • 三种方式对比:怎么选?
    • 额外提醒:这些情况也会导致消息无法路由
    • 最后总结下

用 RabbitMQ 做消息队列时,偶尔会遇到“消息发出去了,但消费者没收到”的情况。排查半天发现,是消息没法被路由到指定队列——比如交换机和队列没绑定、路由键不匹配之类的。那这些无法路由的消息到底去哪了?是直接丢了还是有别的归宿?今天就用大白话聊聊这个问题,再配点代码示例,实操起来更清楚。

先搞懂:啥是“无法路由的消息”?

简单说,就是生产者把消息发给交换机(Exchange)后,交换机根据自身类型(比如 Direct、Topic)和路由键(Routing Key),找不到任何匹配的绑定队列,这时候消息就成了“无家可归”的无法路由消息。

举个最常见的例子:

  • 建了一个 Direct 类型的交换机direct_exchange
  • 没给这个交换机绑定任何队列
  • 生产者往这个交换机发消息,指定路由键test_key

这时候交换机没法把消息转发到任何队列,消息就成了无法路由的状态。

无法路由消息的3个“归宿”:看配置决定命运

消息不会凭空消失,它的去向完全由交换机的两个参数决定——mandatoryalternate-exchange(简称 AE 交换机)。不同配置组合,消息的结局完全不一样。

1. 默认情况:直接丢弃(最容易踩坑)

如果没设置mandatory=true,也没指定 AE 交换机,无法路由的消息会被 RabbitMQ 直接丢弃,连个痕迹都不留。

我刚用 RabbitMQ 时就踩过这个坑,消息发出去没报错,但消费者就是收不到,查了半天才知道是路由失败被丢了。

用 Java 代码示例看看默认行为(Spring Boot 环境):

// 1. 配置 Direct 交换机(没绑定队列,没设 AE)@BeanpublicDirectExchangedefaultDirectExchange(){// 没指定 alternate-exchange,mandatory 默认为 falsereturnnewDirectExchange("direct_exchange_default",true,false);}// 2. 生产者发送消息@AutowiredprivateRabbitTemplaterabbitTemplate;publicvoidsendUnroutableMsg(){Stringmsg="这是一条无法路由的消息";// 交换机没绑定队列,路由键 test_key 找不到匹配项rabbitTemplate.convertAndSend("direct_exchange_default","test_key",msg);System.out.println("消息发送成功");// 控制台显示发送成功,但实际已被丢弃}

这种情况下,控制台不会报错,生产者也不知道消息丢了,排查起来特别麻烦。我们的经验是,除非业务允许消息丢失,否则千万别用默认配置。

2. mandatory=true:退回给生产者

如果设置mandatory=true,交换机发现消息无法路由时,不会直接丢弃,而是会把消息退回给生产者,同时附带退回原因。

这样生产者就能知道消息没路由成功,还能做后续处理(比如重试、存入数据库、打印日志)。

还是用 Java 代码示例,分两步配置:

第一步:生产者发送时设置 mandatory=true
@BeanpublicDirectExchangemandatoryDirectExchange(){returnnewDirectExchange("direct_exchange_mandatory",true,false);}publicvoidsendMandatoryMsg(){Stringmsg="需要退回的无法路由消息";// 设置 mandatory=true,开启退回机制rabbitTemplate.convertAndSend("direct_exchange_mandatory","test_key",msg,message->{message.getMessageProperties().setMandatory(true);returnmessage;});}
第二步:配置退回回调,接收退回的消息
@BeanpublicRabbitTemplate.ConfirmCallbackconfirmCallback(){return(correlationData,ack,cause)->{if(!ack){System.out.println("消息发送失败,原因:"+cause);}};}// 关键:配置退回回调@BeanpublicRabbitTemplate.ReturnsCallbackreturnsCallback(){returnreturned->{System.out.println("消息被退回:");System.out.println("交换机:"+returned.getExchange());System.out.println("路由键:"+returned.getRoutingKey());System.out.println("退回原因:"+returned.getReplyText());System.out.println("消息内容:"+newString(returned.getMessage().getBody()));// 这里可以加后续处理,比如重试发送、存入失败表};}// 注入回调@PostConstructpublicvoidinitRabbitTemplate(){rabbitTemplate.setConfirmCallback(confirmCallback());rabbitTemplate.setReturnsCallback(returnsCallback());}

这样一来,无法路由的消息会被退回,生产者能明确感知,还能做补救措施。我认为这种方式适合对消息可靠性要求高的场景,比如订单、支付相关的消息。

3. 配置 AE 交换机:转发到“备胎”交换机

如果不想退回给生产者,也不想丢弃,可以给原交换机指定一个“备胎”——AE 交换机(Alternate Exchange)。当原交换机无法路由消息时,会把消息转发到 AE 交换机,再由 AE 交换机路由到它绑定的队列。

这种方式适合需要保存无法路由消息,后续分析原因的场景,比如日志收集、数据统计。

配置步骤很简单,还是用代码示例:

第一步:配置 AE 交换机和对应的队列
// 1. 配置 AE 交换机(通常用 Fanout 类型,方便广播所有无法路由的消息)@BeanpublicFanoutExchangeaeExchange(){returnnewFanoutExchange("ae_exchange",true,false);}// 2. 配置 AE 交换机绑定的队列(用来存储无法路由的消息)@BeanpublicQueueaeQueue(){returnnewQueue("ae_queue",true);}// 3. 绑定 AE 交换机和队列@BeanpublicBindingaeBinding(){returnBindingBuilder.bind(aeQueue()).to(aeExchange());}
第二步:原交换机指定 AE 交换机
// 关键:在原交换机中指定 alternate-exchange 为 ae_exchange@BeanpublicDirectExchangemainExchangeWithAE(){returnExchangeBuilder.directExchange("main_exchange_ae").durable(true).alternate("ae_exchange")// 指定 AE 交换机.build();}
第三步:生产者发送消息
publicvoidsendMsgToMainWithAE(){Stringmsg="会被转发到 AE 交换机的消息";// 不需要设置 mandatory,AE 会自动接管rabbitTemplate.convertAndSend("main_exchange_ae","test_key",msg);}

这时候,原交换机无法路由的消息会被转发到ae_exchange,再存入ae_queue队列。我们可以启动一个消费者监听ae_queue,就能收集所有无法路由的消息,后续排查路由配置问题。

三种方式对比:怎么选?

我整理了一个简单的对比表,根据业务场景选就行:

配置方式消息去向优点缺点适用场景
默认(无 mandatory + 无 AE)直接丢弃简单,无额外开销消息丢失,无痕迹允许消息丢失的场景(如日志通知)
mandatory=true退回生产者实时感知,可补救生产者需处理退回逻辑消息可靠性要求高(订单、支付)
配置 AE 交换机转发到 AE 队列保存消息,便于排查需额外维护 AE 交换机和队列需保存无法路由消息(日志收集、数据分析)

额外提醒:这些情况也会导致消息无法路由

除了交换机没绑定队列、路由键不匹配,还有几种常见情况会让消息成为无法路由的“孤儿”:

  • 队列被删除了,但交换机还在,生产者继续往交换机发消息
  • Topic 交换机的路由键格式不匹配(比如队列绑定#.test,生产者用test.a发送)
  • 交换机类型选错(比如用 Direct 交换机,却想按模糊匹配路由)

我们的经验是,遇到消息丢失时,先查交换机和队列的绑定关系,再查路由键是否正确,最后看是否配置了 mandatory 或 AE——大部分问题都能解决。

最后总结下

RabbitMQ 中无法路由的消息,命运完全由我们的配置决定:默认丢弃、退回生产者、转发到 AE 交换机。

我认为核心要点就几个:

  1. 别依赖默认配置,除非明确允许消息丢失
  2. 对可靠性要求高的场景,用mandatory=true + 退回回调
  3. 需保存无法路由消息的场景,配置 AE 交换机
  4. 定期监听 AE 队列,排查路由配置问题,避免大量消息堆积

其实这个问题不难,关键是搞懂 mandatory 和 AE 交换机的作用,再根据业务场景选择合适的配置。如果大家有其他处理无法路由消息的技巧,也欢迎一起交流~

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询