消息设了expiration却未进死信队列,根本原因是死信链路断裂:x-dead-letter-exchange未声明、routing-key不匹配、死信队列非durable或未正确绑定,导致过期消息无法路由。

RabbitMQ 原生不支持延迟消息,Go 里用 Expiration 或 x-message-ttl 都只是模拟——核心靠的是“让消息在队列里躺够时间再踢出去”,不是真等几秒再投递。
为什么消息设了 Expiration 却没进死信队列?
常见现象:消息发进普通队列后不动、不消失、也不转发到目标队列。本质是死信链路断了。
-
x-dead-letter-exchange必须是非空字符串,且该交换机得提前声明(类型通常为direct或topic),不能只写名字却不创建 -
x-dead-letter-routing-key要和后续绑定死信队列时用的routing_key完全一致,大小写、下划线、空格都不能差 - 死信队列本身必须
durable: true,否则服务重启后队列丢失,过期消息无处可去 - 消费者连的必须是死信队列(比如叫
delayed_orders),不是原始队列——连错队列就收不到
QueueDeclare 里怎么安全设队列级 TTL?
比每条消息单独设 Expiration 更稳定,避免 AMQP 协议层开销上升,也规避消息级 TTL 被覆盖的风险。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
amqp.Table{"x-message-ttl": 30000}传给QueueDeclare的args参数,单位是毫秒 - 不要同时在队列和消息上设 TTL,否则以更短的那个为准——比如队列设了 20s,消息设了 5s,实际按 5s 过期
- 改了队列参数?RabbitMQ 不允许运行中修改,必须删掉旧队列再重新
QueueDeclare,否则channel.Publish会直接报Exception (504) Reason: "channel/connection is not open"
Go 生产者发延迟消息时容易漏的关键点
很多人只关注 Expiration 字段,却忘了它只是“触发条件”之一,真正决定是否进死信队列的是队列配置。
-
Expiration是字符串,不是整数:"3000"✅,3000❌(会静默失效) - 必须确保该消息投递的目标队列已正确声明死信属性(
x-dead-letter-exchange等三个 key 缺一不可) - 如果用默认交换机(
"")直投队列,要确认routing_key和队列名完全一致,否则消息根本进不了那个队列 - 调试时优先用
rabbitmqctl list_queues name messages_ready messages_unacknowledged查普通队列积压;再看死信队列长度是否同步增长——不增长基本就是路由断了
最常被忽略的是:死信交换机必须显式绑定到死信队列,而且绑定用的 routing_key 必须和队列声明里的 x-dead-letter-routing-key 严丝合缝。少一个字符,消息就卡死在原地。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










