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