RabbitMQ 死信队列
死信(Dead Letter)指"没能按正常路径被消费掉的消息"。RabbitMQ 允许给队列绑定一个死信交换机(DLX),消息变成死信时自动转投过去,再由它路由进专门的死信队列,供程序补偿或人工处理。
死信的三种来源
消息满足下列任一条件即成为死信:
| 来源 | 触发条件 | 常见原因 |
|---|---|---|
| 消息被拒且不重回 | basicReject/basicNack 且 requeue=false | 业务校验失败、重试多次后放弃 |
| 消息过期 | TTL 到期且位于队头 | 见消息过期 TTL 章节 |
| 队列长度超限 | 超过 x-max-length 等上限被挤出 | 积压导致队头消息被挤掉 |
转投死信是可选的:队列没配死信交换机时,上述消息会被直接丢弃。
配置死信交换机
在声明业务队列时通过 x-dead-letter-exchange 指定 DLX,可再用 x-dead-letter-routing-key 指定转投时的 routing key(缺省沿用消息原 routing key):
# 1. 先声明 DLX 与死信队列(DLX 就是普通交换机)
rabbitmqadmin declare exchange name=dlx.ex type=fanout durable=true
rabbitmqadmin declare queue name=q.dlx durable=true
rabbitmqadmin declare binding source=dlx.ex destination=q.dlx routing_key=""
# 2. 声明业务队列并挂上死信交换机
rabbitmqadmin declare queue name=q.biz \
arguments='{"x-dead-letter-exchange":"dlx.ex"}'
注意:DLX 必须真实存在且绑定到位,否则死信投递会静默失败(消息丢失);已声明队列的 x-dead-letter-* 参数无法修改,只能删队列重建。
完整链路示例
典型流程:业务消费者处理失败且不重回 → 消息进 DLX → 路由到死信队列 → 死信消费者负责补偿、告警或记录:
# 业务消费者:处理失败且放弃重试时 nack(requeue=False) 转死信
ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)
# 死信消费者:收到死信后告警并落库(补偿/对账入口)
def on_dlx(ch, method, props, body):
print("死信消息:", body) # 输出:死信消息: b'order 10086 fail'
alert_and_save(body) # 通知值班并记录原因
ch.basic_ack(delivery_tag=method.delivery_tag)
ch.basic_consume("q.dlx", on_message_callback=on_dlx, auto_ack=False)
死信常用于:消费重试达到上限后的终态处理、业务告警、人工补偿队列,也是下一章延迟队列的基石。
查看死信原因:x-death 头
消息被转死信时,broker 会往消息 headers 里写入 x-death 数组,记录原因(reason 取 rejected、expired、maxlen 之一)、原队列、转出时间、原交换机、routing keys 与次数。用 rabbitmqadmin 抓一条死信即可查看:
rabbitmqadmin get queue=q.dlx count=1 ackmode=ack_requeue_false
# 输出中含 "x-death": [{"reason": "rejected", "queue": "q.biz", ...}]
多次转死信时 x-death 会叠加记录,可据此判断消息的死信轨迹与次数,超过阈值再决定丢弃。
提醒:死信转发会打乱消息顺序(相当于过期后再入队),对顺序敏感的消息不要依赖死信保序。
小结
死信队列把"消费失败/过期/超长挤掉"的消息统一收容:队列声明时配 x-dead-letter-exchange 与 x-dead-letter-routing-key,死信消费者读取 x-death 头拿到原因后做补偿或告警,是 MQ 可靠性的最后一道兜底。