bigywater.tech
RabbitMQ · 11

延迟消息:TTL + 死信的做法与队头阻塞问题

没有插件时,RabbitMQ 做延迟消息靠“TTL + 死信”。这样做能准到多少毫秒?给每条消息单独设 expiration 为什么会翻车?延迟队列要怎么设计,消息躺在里面占多少资源?这篇都量一遍。

+2 ms
队列 TTL=1000 ms 时,消息到达死信队列的实际延迟(1002 ms)
3004 ms
先发 3000 ms 再发 300 ms 的消息时,后者实际死信的时刻——被队头挡住
48.8 MiB
50000 条 1 KB 的延迟消息躺在队列里的数据量,队列进程自己只占 0.7 MiB

做法

实测 · 本机执行输出
  发送时刻(ms): [0, 304, 612]
  到达时刻(ms): [1002, 1307, 1614];实际延迟(到达 − 发送): [1002, 1003, 1002] ms

消息发到一个没有消费者的延迟队列,队列设 x-message-ttl 和 x-dead-letter-exchange;过期后死信被转到真正的业务队列。实测延迟比设定值只多几毫秒。

队头阻塞

实测 · 本机执行输出
  classic 队列:到达死信队列的 (时刻 ms, 消息): [(3004, 'long(3000ms)'), (3004, 'short(300ms)')]
  quorum 队列:到达死信队列的 (时刻 ms, 消息): [(3006, 'long(3000ms)'), (3006, 'short(300ms)')]

队列只在队头检查是否过期。每条消息单独设 expiration 时,TTL 短的排在 TTL 长的后面,就要等前面的先过期。classic 和 quorum 队列都是如此。

顺序

实测 · 本机执行输出
  到达死信队列的 (时刻 ms, 消息): [(303, 'short(300ms)'), (3003, 'long(3000ms)')]

如果先短后长,各自准时;但发布顺序无法保证,所以不能依赖。

一档一队列

实测 · 本机执行输出
  几乎同时发出,到达死信队列的 (时刻 ms, 消息): [(303, 'b(300)'), (303, 'd(300)'), (1003, 'c(1000)'), (1003, 'f(1000)'), (2005, 'a(2000)'), (2005, 'e(2000)')]

每种延迟时间建一个队列、用队列级 TTL,同一队列里 TTL 相同,先进先出就是先过期,没有阻塞。代价是延迟时间只能取预先定义的几档。

精度

实测 · 本机执行输出
  收到 200 条;延迟 min 501 / p50 503 / p99 506 / max 506 ms(目标 500)

队列 TTL=500 ms 时延迟实测 501~506 ms。队列压满消息、broker 忙时的精度没有测。

成本

实测 · 本机执行输出
  big.q: messages=50000 memory=0.7 MiB message_bytes=48.8 MiB
  队列进程自己占的内存很小(消息体在磁盘上),但 50000 条、约 48.8 MiB 的数据要在队列里一直躺 60 秒;没有单独的“延迟存储”

延迟期间消息一直占着队列(磁盘和索引),量大、延迟长的场景要算上这部分。

分步演示

延迟消息
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / RabbitMQ 4.3.6 / Erlang/OTP 28.5 / pika 1.4.4(Homebrew 与临时虚拟环境);所有节点都是本机自己起的 RabbitMQ 进程(127.0.0.1,端口 9500 以上,每个实验独立的数据目录与节点名,不连任何外部服务);命令输出来自真实的 rabbitmqctl 与管理 API,发布与消费用 pika 的真实连接;计时用 time.perf_counter,数字每次运行会有小幅抖动。延迟精度用 perf_counter 计发送与到达的时刻,到达时刻由死信队列上的消费者回调记录;第五节发 200 条(每隔 50 ms),第六节 50000 条 1 KB 的持久化消息,等 2 秒后用 rabbitmqctl list_queues 读 messages、memory、message_bytes
    • 没有验证:社区的延迟消息交换机插件(rabbitmq_delayed_message_exchange,Homebrew 的 4.3.6 里没有这个插件);更长的延迟(分钟、小时)下队列的行为;broker 重启后延迟消息 TTL 的重新计时;队列里堆很多消息时过期扫描的精度;quorum 队列上 TTL 过期的精度(只看了队头阻塞)
    京ICP备2026067502号-1京公网安备11010502063487号