2026-11-02·by Sijie Wang#fundamentals#networking#keepalive

keepalive-two-layers

两层保活:机器还在吗,程序还能干活吗

一条闲着的长连接,没法告诉你对面是否还存在。而"活着"其实是两个不同的问题,两层保活各答一个——谁也替不了谁。

两种死法。 (1) 机器或路没了:宕机、断网——外加一个隐形的变体:这条连接的 NAT 映射过期了(见 how-nat-works),路悄无声息地不存在了。(2) 机器好好的,程序不行了:进程卡死、死锁、事件循环堵住。操作系统健康,应用不干活。

第一层——TCP keepalive——查机器和路。 内核在闲置够久后自发探测包,对面的内核回话。这证明了主机和路由还在。它看不见死法 (2):进程卡成僵尸,内核照样替它回探测,你会以为一切正常。它的另一个毛病:默认第一次探测在闲置两小时后才发——真靠它的应用都要把这个数调小。

第二层——应用层心跳——查程序本身。 应用自己定时交换小帧(WebSocket 的 ping/pong、HTTP/2 的 PING、gRPC 的 keepalive)。只有对面的应用能回话,所以回话即证明它的事件循环在转、它接得住活。这一层还兼第二份工:周期性流量把 NAT 映射和负载均衡的闲置计时器不断续上,连接不至于被中间设备回收。WireGuard 那个 25 秒的 persistent keepalive,主要就是为这第二份工存在。

出没场景。 数据库连接池(池中连接夜里被中间设备掐死、第二天被当好连接发出去——池的探活与调过的 keepalive 就是来提前剔除它的);云负载均衡(闲置超时是定数、执行时不通知任何人——心跳间隔比它长,连接就无声无息地死);移动端即时通讯(靠心跳发现进隧道、切网络的断连并驱动重连;"在线"绿点就是这一层被直接画到了界面上);gRPC 服务间长连接(两边 ping 参数要对齐,客户端太积极会被服务端以"ping 太多"掐掉);物联网设备群(心跳兼任设备名册的保鲜信号)。

间隔怎么平衡——先三条约束,再三个旋钮。 发现延迟 ≈ 间隔 + 判死窗口,成本 ≈ 单跳大小 × 频率 × 机队规模。间隔不是自由选的:(a) 路径上最没耐心的中间设备定死上限——心跳必须勤过全程最短的闲置超时(NAT 映射、LB 的线),不然连接照样静默死亡;(b) 业务能容忍多晚发现死亡定目标——在线状态晚一分钟没事,选主接管晚十秒是事故;(a)(b) 谁严听谁;(c) 机队规模下的总成本——一百万台设备 30 秒一跳,就是每秒约 3.3 万条纯心跳开销,手机把全机应用合并成一条系统推送通道就是这个账逼的。在约束之内再调:连续漏 2–3 拍才判死(一拍多半是天气,不是死亡);全体加 ±10–20% 随机抖动,免得机队整齐划一地同时发、在服务器上每间隔砸出一个小洪峰;重连退避与心跳分开走,免得服务器刚恢复就被全队同时重连再踩死一次。经验配方:间隔取路径最短闲置超时的五到八成,漏 2–3 拍判死,开抖动,重连走退避。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →