bigywater.tech
RabbitMQ · 23

自动重连与拓扑恢复

连接断了以后谁来恢复:连接、channel、exchange、队列、绑定、消费者,还有没发完的消息?不同客户端库差别很大。这篇用 pika 和 aio-pika 在同一个 broker 上做断线实验,看哪些被自动恢复,哪些要应用自己做。

6.05 s
broker 重启期间消费端的最大间隔(停机 1.5 s + 启动 4.9 s),之后自动恢复
0 / 0
robust 消费者收到 728 条,重复 0 条、丢失 0 条
510 次
停机期间发布端的失败次数(ChannelInvalidStateError),需要应用自己重试

没有自动恢复

实测 · 本机执行输出
  消费者在线时收到: ['before']
  $ rabbitmqctl close_connection "<pid>" "closed by operator"
  之后 process_data_events: ConnectionClosedByBroker: (320, 'CONNECTION_FORCED - closed by operator')
  连接状态: is_open=False;另一个连接又发了 "after" 一条,队列里现有 (ready, unacked) = (1, 0)——没有消费者了,消息停在队列里

pika 的 BlockingConnection 不会重连,连接被关后异常抛给调用者,所有对象都要应用重建。

robust 连接

实测 · 本机执行输出
  broker 关闭用了 1.5 s,重新启动到可用用了 4.9 s
  broker 重新可用之后,消费者收到的第一条消息在 0.03 s 后(reconnect_interval=1 s)
  发布端:确认成功 728 条,失败/超时 510 条(异常类型 ['ChannelInvalidStateError']);消费端:共收到 728 条,重复 0 条,redelivered=True 的 0 条
  已确认发送但没收到的序号: [](共 0 条)
  消费端相邻两条消息的最大间隔: 6.05 s(= 停机 + 重连期间的中断)

aio-pika 的 connect_robust 会按 reconnect_interval 重连,并把声明过的 exchange、队列、绑定和消费者重新声明。消费端无重复无丢失,持久消息一条不少;但发布端不一样——连接断开期间的 publish 直接失败(停机期间 510 次),robust 连接不会替你缓冲,应用要自己重试并考虑幂等。

队列被删除

实测 · 本机执行输出
  另一个客户端删除队列 dq 之后,消费者收到: ['Cancel'](Basic.Cancel);channel 仍然开着: is_open=True
  这种取消不会触发连接级的重连逻辑,客户端必须自己监听 cancel 并重新声明队列、重新订阅

broker 发 Basic.Cancel,连接和 channel 不受影响,客户端要自己监听并重新订阅。

恢复的是什么

实测 · 本机执行输出
  排他队列(aio-pika 生成的名字 amq_…)重启后按原名 passive 声明: 存在(被 robust 客户端重新声明)
  durable 的 keepme 重启后: keepme(本来就在磁盘上)

恢复的是拓扑定义和订阅,不是数据:非持久队列在重启后是空的,未确认的发布结果不确定。

分步演示

自动重连
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。第一节用 rabbitmqctl close_connection 关掉消费者连接;第二节 aio-pika(connect_robust,reconnect_interval=1)的发布者带 publisher confirm,每 10 ms 一条;关闭 broker 用 rabbitmqctl shutdown,再启动;序号核对收发的重复与丢失
    • 没有验证:kill -9 突然死亡时的恢复(这里是正常关闭);指数退避的重连策略与重连风暴;集群里故障转移到其他节点(客户端地址列表);发布端自己实现重试后产生的重复;Java / Go 等客户端库的恢复行为;消费者在重连窗口里已处理未确认的消息会重复投递(这次没有出现 redelivered)
    京ICP备2026067502号-1京公网安备11010502063487号