java消费端通过rabbitmq自动将失败或超时消息路由至dlq,需配置业务队列的x-dead-letter-exchange、x-dead-letter-routing-key等参数,并在手动ack模式下调用basicnack(deliverytag, false, false)确保requeue=false。

Java 消费端通过死信队列(DLX)处理异常消息,核心不是“主动发送”到 DLQ,而是让 RabbitMQ 自动把处理失败或超时的消息路由过去。关键在两头:一端是业务队列的声明参数要配对,另一端是消费者行为要符合触发条件。
配置业务队列支持死信转发
业务队列必须显式设置死信相关参数,否则消息再失败也不会进 DLQ:
-
x-dead-letter-exchange:填已声明的死信交换机名(如
"dlx.exchange"),必须存在且类型匹配(常用direct) -
x-dead-letter-routing-key:显式指定路由键(如
"order.fail"),避免依赖默认行为导致投递失败 - 可选加 x-message-ttl 实现超时兜底(如
60000表示 1 分钟未消费即转 DLQ) - 避免用空字符串交换机(
"")做 DLX,路由键固定为队列名,易冲突、难维护
消费者正确拒绝消息触发 DLQ
手动 ACK 模式下,消费者需明确告知 RabbitMQ “不重试、不回队”,才能让消息变成死信:
- 处理失败时,调用
channel.basicNack(deliveryTag, false, false)或basicReject(deliveryTag, false) - 第二个参数
requeue=false是关键,设为true会重回原队列,无法进 DLQ - Spring AMQP 中若用
@RabbitListener,需设acknowledge-mode: manual,并在代码中调用channel.basicNack() - 不要只依赖
@Retryable客户端重试——重试失败后仍需手动nack(requeue=false),否则消息不会进 DLQ
带重试次数控制的异常兜底
防止无限重试压垮服务,可在消息头里记录并判断重试次数:
- 首次消费失败时,从
message.getMessageProperties().getHeaders().get("retry-count")读取当前值(初始为null或0) - 若重试次数 retry-count + 1 写入新消息头,重新发布回原队列(可加短 TTL 实现退避)
- 若已达阈值(如 3 次),直接
basicNack(..., false, false),消息立刻进 DLQ - DLQ 中的消息应保留原始 header 和 body,方便后续分析根因(如下游超时、JSON 解析失败)
监听死信队列做后续动作
DLQ 不是终点,而是异常可观测性的起点:
- 单独声明一个消费者监听
dlx.queue,打印日志、落库、发告警 - 对可恢复错误(如临时网络抖动),可从 DLQ 中读取消息,修正后重新投递到业务队列
- 定期导出 DLQ 消息内容,人工排查高频失败类型,推动上游或下游优化
- 避免在 DLQ 消费者里再抛异常导致消息堆积——至少保证日志记录和基础告警
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











