RabbitMQ 延迟队列

延迟队列让消息"过一段时间才被投递",常用于订单超时关单、定时提醒等。RabbitMQ 本身没有内置延迟队列,主流做法有三种:TTL+死信组合、官方延迟插件、应用侧定时任务。

三种方案对比

方案实现成本延迟精度适用
TTL + 死信低(零插件)毫秒级,受队头阻塞影响固定几种延迟时长
官方延迟插件中(需启用插件)秒级调度,一般够用每条消息延迟可不同
应用定时任务高(要建任务表)由轮询周期决定海量延迟任务的兜底

方案一:TTL + 死信(经典方案)

核心思路:消息先进一个无人消费、带 x-message-ttl 的延迟队列;TTL 到期后消息被当作死信转投到目标交换机,从而"延迟后"到达真正消费它的队列:

# 1. 声明业务交换机、业务队列并绑定
rabbitmqadmin declare exchange name=biz.ex type=direct durable=true
rabbitmqadmin declare queue name=q.order durable=true
rabbitmqadmin declare binding source=biz.ex destination=q.order routing_key=order.close
# 2. 声明延迟队列:10 秒 TTL,到期转死信到 biz.ex
rabbitmqadmin declare queue name=q.delay.10s \
    arguments='{"x-message-ttl":10000,"x-dead-letter-exchange":"biz.ex","x-dead-letter-routing-key":"order.close"}'

生产者把消息发进延迟队列(直接按队列名投递即可):

import pika
ch = pika.BlockingConnection(pika.ConnectionParameters("127.0.0.1")).channel()
ch.basic_publish(exchange="", routing_key="q.delay.10s",
                 body=b"close order 10086")

10 秒后消息过期 → 转死信到 biz.ex(routing key=order.close)→ 落入 q.order 被正常消费。

注意两点:x-message-ttl 对整队列统一,需要几种延迟时长就建几个延迟队列(如 10s、30s、5m 各一个);消息经历过一次"过期再入队",转发顺序不保证,延迟期间也无法从队列中撤单,消费端要按业务状态(是否已支付)自行判断是否执行。

方案二:官方延迟插件

官方提供 rabbitmq_delayed_message_exchange 插件,启用后声明一种 x-delayed-message 类型交换机,消息带 x-delay 头即按毫秒延迟投递:

rabbitmq-plugins enable rabbitmq_delayed_message_exchange
# 输出:Enabling plugins on node rabbit@myhost ... done
# 提示:启用该插件需要重启 broker 生效
# 声明延迟交换机:x-delayed-type 指定底层路由类型(direct/topic/fanout)
rabbitmqadmin declare exchange name=delay.ex type=x-delayed-message \
    arguments='{"x-delayed-type":"direct"}'
import pika
ch = pika.BlockingConnection(pika.ConnectionParameters("127.0.0.1")).channel()
props = pika.BasicProperties(headers={"x-delay": 30000})   # 延迟 30 秒(毫秒)
ch.basic_publish(exchange="delay.ex", routing_key="order.close",
                 body=b"close order 10086", properties=props)

插件由官方维护发布,延迟期内消息由插件内部保管、到期再按 x-delayed-type 的路由规则转发,每条消息可单独指定延迟时长,适合"延迟各不同"的场景;插件版本需与 broker 配套,升级前先查兼容性。

精度与选择建议

TTL+死信的精度由 broker 队头检查决定,理论上可到毫秒级,但队头阻塞会让实际延迟偏大;插件按内部调度到期转发,一般按秒级精度评估。延迟消息量大且时长很长的任务,两种 MQ 方案都会长期占资源,可考虑应用侧定时任务表轮询兜底。

典型场景

  • 订单超时自动关单:下单后 30 分钟发延迟消息,消费时查支付状态,未支付才关单;
  • 定时提醒:注册 24 小时后发送召回提醒;
  • 重试退避:失败消息按递增延迟重新入队重试。

小结

延迟队列本质是把"到期再投"转译成过期死信或插件延迟:固定时长选 TTL+死信,逐条可变延迟选官方 x-delayed-message 插件;所有方案都要在消费端用业务状态兜底,避免延迟消息到点后误执行。

笔记加载中…