1. 原生 RabbitMQ 机制:依赖 Ack 和“丢回去”(Requeue)
RabbitMQ 服务端本身没有内置“最多重试 N 次然后丢弃”这种复杂的本地计数机制。它主要依赖**消息确认机制(ACK)**来保证消息不丢失。
当消费者处理失败时,通常会有以下几种情况:
- 明确拒绝并要求重入队(nack / reject, requeue=true): 消费者告诉 RabbitMQ “我处理失败了,把它塞回队列吧”。此时,消息会被丢回原队列。RabbitMQ 会尝试将其投递给下一个空闲的消费者(如果只有一个消费者,那就是重新投递给它自己)。
- 风险: 如果是代码逻辑错误导致的处理失败(比如空指针异常),这会导致消息无限循环投递,疯狂消耗 CPU 和网络资源。
- 明确拒绝并丢弃(nack / reject, requeue=false): 消费者告诉 RabbitMQ “我处理失败了,这消息没救了,扔了吧”。此时消息会被丢弃,或者(如果配置了的话)进入死信队列(DLX)。
- 消费者直接断开连接:
如果消费者在发送 ACK 之前宕机或网络断开,RabbitMQ 会认为消息未被处理,并自动将其重新放入队列,投递给其他消费者。
结论: 在纯原生层面,处理失败主要是**“丢回去换消费者(或同一个消费者)重试”**。
2. 客户端框架机制(以 Spring AMQP/Spring Boot 为例):本地重试
因为原生 RabbitMQ 的无限 requeue 很容易引发灾难,所以现代的框架(如 Spring Boot)在客户端层面做了一层强大的封装。
当你在应用中开启了重试机制(例如在 application.yml 中配置 spring.rabbitmq.listener.simple.retry.enabled=true)后,行为就变成了本地重试:
- 如果本地重试了 3 次依然失败,Spring 默认的策略是抛出 AmqpRejectAndDontRequeueException。
- 这会触发向 RabbitMQ 发送 nack 且 requeue=false 的指令。
- 此时,消息不会被丢回队列去祸害别的消费者,而是被丢弃或进入死信队列(Dead Letter Exchange, DLX)。
结论: 在框架层面,默认行为是**“本地重试”**,重试次数耗尽后彻底拒绝。
实践
在实际的生产架构中,为了保证系统的高可用和防止雪崩,通常推荐的组合拳是:





