bigywater.tech
RabbitMQ · 22

心跳与连接断开检测

心跳是发现“连接其实已经死了”的唯一办法。它怎么协商、线上长什么样、发现一个悄悄消失的对端要多久?关掉心跳会怎样?为什么一个“耗时任务”会让连接莫名其妙断开?这篇用代理做黑洞,一项项量。

6.1 s
heartbeat=2 的连接遇到网络黑洞后,broker 记下 missed heartbeats 的时间(约 3 倍超时)
8 字节
一个 Heartbeat 帧的大小,每半个超时发一次
0
heartbeat=0 时黑洞 25 秒后 broker 对连接的处理:什么都不做,连接仍是 running

协商

实测 · 本机执行输出
  客户端请求 heartbeat=None → list_connections 的 timeout 列(协商结果): 60
  客户端请求 heartbeat=0    → list_connections 的 timeout 列(协商结果): 0
  客户端请求 heartbeat=5    → list_connections 的 timeout 列(协商结果): 5
  客户端请求 heartbeat=30   → list_connections 的 timeout 列(协商结果): 30
  客户端请求 heartbeat=120  → list_connections 的 timeout 列(协商结果): 120

broker 在 Connection.Tune 里给出默认 60 秒,客户端 TuneOk 回自己的值,实测客户端请求的 0、5、30、120 都被采用。

帧

实测 · 本机执行输出
  C→S: 10 个 Heartbeat,每个 8 字节,相邻间隔(秒): [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.01, 1.0, 1.0]
  S→C: 10 个 Heartbeat,每个 8 字节,相邻间隔(秒): [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.01, 1.0, 1.0]
  间隔约 1 秒 = heartbeat 超时的一半(2 s ÷ 2);空闲时双方都在发,忙的时候别的帧也算“活着”,不再另发

Heartbeat 帧只有 8 字节,双方各自每 timeout/2 发一次;连接上有别的数据时同样算“活着”。

发现死连接

实测 · 本机执行输出
  heartbeat=2 的连接,黑洞开始后 6.1 秒,broker 日志出现: <0.1103.0> missed heartbeats from client, timeout: 2s
  list_connections 此时: ['Listing connections ...']

对端消失(黑洞)时,broker 约在超时的 3 倍(6.1 s)后记 missed heartbeats 并关闭连接。

关掉心跳

实测 · 本机执行输出
  heartbeat=0,黑洞 25 秒后 list_connections: ['state\ttimeout', 'running\t0'](连接还在,state=running,timeout=0)

heartbeat=0 时黑洞 25 秒后连接仍然是 running,broker 发现不了,连接和它的 channel、消费者都还占着。

线程被阻塞

实测 · 本机执行输出
  8 秒后 broker 日志里新增的 missed heartbeats 行数: 1;list_connections: ['Listing connections ...']
  客户端醒来后再发一个命令: StreamLostError: Stream connection lost: BrokenPipeError(32, 'Broken pipe')
  解决办法:耗时任务放在别的线程里、主线程继续 process_data_events;或者 BlockingConnection 里分段调用 process_data_events;或把 heartbeat 调大(但检测对端死亡也变慢)

BlockingConnection 不在后台线程里发心跳,只在收发调用里处理。线程忙于别的事超过两个心跳间隔,连接就会被 broker 关闭,下一次操作得到 StreamLostError。

分步演示

心跳
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。第二、三、四节经过自己写的 TCP 代理(hole=True 时停止转发但不断开 TCP);协商结果来自 rabbitmqctl list_connections timeout;“missed heartbeats”来自 broker 日志;第五节客户端 time.sleep 8 秒模拟忙
    • 没有验证:TCP keepalive 对 heartbeat=0 的补救作用;多次测量 “3 倍超时” 的分布(只测了 2 次,6.0 与 6.1 秒);TLS 连接上的心跳;客户端库自己实现后台心跳线程(如 aio-pika / Java 客户端)的行为;非常大的 heartbeat 值;服务器端 heartbeat 默认值的修改
    京ICP备2026067502号-1京公网安备11010502063487号