bigywater.tech
RabbitMQ · 36

消费者崩溃后未确认消息的重新投递

消费者拿了消息没来得及确认就挂了,消息去哪里、多久回来、会不会无限重投?进程被杀和网络不通有什么差别?quorum 队列怎么限制重试?这篇把本系列前面几篇的零散结论补成完整的一套。

381 ms
kill -9 消费者进程后,100 条未确认消息全部回到队列的时间
8.6 s
网络黑洞下(heartbeat=3)同样的消息回队列的时间
3 次
x-delivery-limit=3 的 quorum 队列对 poison 消息重投的次数,之后丢弃

进程被杀

实测 · 本机执行输出
  kill 之前 (ready, unacked) = (100, 100)
  kill -9 之后 381 ms,unacked 变回 0;(ready, unacked) = (200, 0)

操作系统关闭 TCP 连接,broker 立即发现,未确认消息很快(约 0.4 s)回到队列。

部分确认

实测 · 本机执行输出
  队列里剩 110 条:redelivered=True 的 60 条(m40~m99)、全新的 50 条(m100~m149);前 40 条(m0~m39)已确认,没有再出现
  顺序: ['m40', 'm41', 'm42', 'm43', 'm44']……——被重新投递的回到了队首,原来的先后顺序保持不变

确认过的消息不会重投;未确认的回到队首并带 redelivered=True,顺序不变。

网络黑洞

实测 · 本机执行输出
  黑洞开始前 (ready, unacked) = (50, 50)
  黑洞开始后 8.6 s,unacked 回到 0;(ready, unacked) = (100, 0)(heartbeat=3 s,约 3 倍超时,和第 22 篇的黑洞检测时间一致)

没有 TCP 关闭时只能靠心跳超时发现,约 3 倍超时(8.6 s),这段时间里未确认的消息被卡着。

毒消息

实测 · 本机执行输出
  每次拿到时 (redelivered, x-delivery-count 头): [(False, None), (True, 1L), (True, 2L), (True, 3L), '(队列里已没有这条消息)']
  x-delivery-limit=3 的队列最终 (ready, unacked) = (0, 0)——超过次数之后消息被丢弃(这里没有配死信交换机)

quorum 队列在重投的消息上带 x-delivery-count,配合 x-delivery-limit 限制重试次数,超过后丢弃或进入死信。

经典队列

实测 · 本机执行输出
  经典队列 5 次 (redelivered, x-delivery-count): [(False, None), (True, None), (True, None), (True, None), (True, None)]

只有 redelivered 布尔值,没有计数;需要限制重试就在应用里数,或用死信重试环。

分步演示

未确认重投
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。消费者是 qcons.py 子进程(pika),收满消息后挂着等被 kill -9;回队列时间是从 kill 到 rabbitmqctl list_queues 的 unacked 变 0(轮询间隔 50 ms,每次读本身要几百毫秒);黑洞经自己写的代理,heartbeat=3;quorum 的重投实验每次取到就断开连接
    • 没有验证:delivery-limit 配合死信交换机(dead-letter-strategy);多节点下 leader 切换时的重投;大量未确认消息(几万)回队列的耗时;stream 队列没有这个机制;自动重试(应用侧)的退避策略;网络黑洞时间的多次统计(这里每次只测一次)
    京ICP备2026067502号-1京公网安备11010502063487号