死信队列实现关键在于缓冲队列正确声明x-dead-letter-exchange、x-message-ttl等参数,消息由rabbitmq自动判定为死信后转发;三种死信场景:消费者显式拒绝(requeue=false)、消息超ttl、队列满被挤出;需先声明dlx交换机与死信队列并绑定,再声明带dlx参数的缓冲队列,消费者必须手动ack或reject。

ThinkPHP5.1 项目中用 php-amqplib 实现死信队列(DLX),核心不是写多少代码,而是把「队列属性」和「消息流转路径」配对。只要缓冲队列正确声明了 x-dead-letter-exchange 和 x-message-ttl(或触发 nack/reject),消息就能自动进死信队列——不需要改 TP 框架,也不依赖插件。
死信产生的三种真实场景必须明确
消息进 DLX 不是“主动送进去”,而是被 RabbitMQ 自动判定为死信后转发。常见原因有且仅有这三种:
- 消息被消费者显式拒绝(
basic_reject或basic_nack)且requeue=false - 消息在队列中存活时间超过
x-message-ttl(毫秒级,比如600000表示 10 分钟) - 队列设置了最大长度(
x-max-length),新消息进来时最老的消息被挤出变成死信
缓冲队列声明必须带 DLX 参数
你不能只在生产者端设 expiration,也不能只在消费者端 catch 异常就完事。关键动作发生在队列创建时——用 php-amqplib 显式传入参数表:
-
x-dead-letter-exchange:填你已声明好的交换机名,比如'dlx_exchange' -
x-dead-letter-routing-key(可选):指定转发时用的 routing key,不填则沿用原消息的 routing key -
x-message-ttl:单位毫秒,只对当前队列生效,例如300000表示 5 分钟
注意:这个队列不能绑定消费者,否则消息会被提前取走,TTL 就失效了。
死信交换机与死信队列要独立声明
DLX 本质是个普通 exchange,死信队列也是普通 queue,但它们要按顺序初始化:
- 先声明死信交换机(类型建议
direct或fanout) - 再声明死信队列,并绑定到该交换机(用对应 routing key)
- 最后声明业务缓冲队列,带上上面两个参数
示例片段(TP5.1 命令行命令中调用):
$ch->exchange_declare('dlx_exchange', 'direct', false, true, false);
$ch->queue_declare('dead_letter_queue', false, true, false, false);
$ch->queue_bind('dead_letter_queue', 'dlx_exchange', 'dlx_route');
$args = new AMQPTable([
'x-dead-letter-exchange' => 'dlx_exchange',
'x-dead-letter-routing-key' => 'dlx_route',
'x-message-ttl' => 600000
]);
$ch->queue_declare('buffer_queue', false, true, false, false, false, $args);
消费者必须手动 ack 或显式 reject
如果用了 no_ack=false(默认),就必须在处理完逻辑后调用 $ch->basic_ack($delivery_info['delivery_tag']);否则 RabbitMQ 认为消息未完成,不会释放也不会进 DLX。
- 处理失败想进死信队列?用
$ch->basic_reject($delivery_info['delivery_tag'], false) - 想重试一次?用
$ch->basic_nack($delivery_info['delivery_tag'], false, true) - 千万别漏掉 tag,否则 ack/reject 失效,队列会堆积、内存暴涨
不复杂但容易忽略:DLX 路径是否通,取决于缓冲队列参数是否完整、死信交换机是否已存在、routing key 是否匹配绑定关系。跑通一次,后续就稳定了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











