摘要
- TCP keepalive 只能询问沉默的对端是否仍在传输层回应,不能证明远端应用健康。
- 连接沉默可能有多种原因,因此该机制保持可选、可配置并默认关闭。
- 一次无回复不能证明死亡;中间设备状态、用户超时与能耗也让通用探测周期失去安全性。
可靠传输遇到没有传输的时刻
RFC 793 给 TCP 建立了一套针对“已经发送之物”的秩序:字节占据序列空间,确认推动发送窗口,缺失触发重传,连接最终成功、被复位,或到达本地设定的失败界限。
空闲连接却没有这样的证据链。没有新字节等待确认,也没有旧字节的重传定时器可以揭示路径中断。两端可能都健康,只是暂时没有业务;也可能有主机崩溃、路由消失、防火墙清除状态,或地址映射过期。从本地 TCP 看,这些现实都可能表现为同一种安静。
这不是可靠性算法失灵,而是一个认识边界:没有尝试交付,就没有交付结果可以观察。
从序列前沿后退一步
RFC 1122 在 1989 年记录 keepalive 时,明确称它仍有争议。经典探测使用 SEG.SEQ = SND.NXT-1,即紧贴下一个新字节之前的序列号。
这个位置是刻意选择的。它不应当成为新的应用数据,却会促使仍保存连接状态的对端 TCP 返回 ACK,说明自己下一步期待什么。通常探测不带数据,也不推进应用字节流;携带一个“垃圾”字节的兼容形式,只为应付当时错误实现,并且必须可以配置。
收到的 ACK 只回答一个很窄的问题:此刻有一个远端 TCP 经由可用的往返路径处理了这个报文。它没有证明远端进程没有死锁、凭证仍有效、数据库可达,或者下一次业务请求一定成功。
可选并非功能缺失,而是安全边界
RFC 1122 没有要求所有 TCP 都实现 keepalive。即便实现,应用也必须能按连接启停,默认状态必须关闭;空闲间隔需要可配置,而默认值不得短于两小时。
这些限制防止传输层在应用不知情时选择失败语义。终端会话、数据库连接池、路由邻接和休眠传感器,对误关闭、发现延迟、网络流量与保留状态的代价判断完全不同。TCP 只看到没有字节,无法从中推导哪一种代价最重要。
两小时从来不是“7,200 秒后对端就死亡”的自然定律。它是一道保守边界:除非应用主动选择,否则实现不应频繁制造未经请求的流量。RFC 9293 汇总现代 TCP 时仍保留这套安排,长期部署并没有把可选探测变成普遍的生命定义。
为什么一次无回答什么也证明不了
纯 ACK 不像应用数据那样拥有独立的可靠重传承诺。探测可能丢失,回复也可能丢失,拥塞还会延迟任何一方。因此 RFC 1122 与 RFC 9293 都禁止把一次未回应当作连接死亡的证据。
约束之所以重要,是因为关闭是一项行动,不是一项观察。TCP 删除状态并向上报告失败后,应用可能放弃交易、释放锁、选举替代节点或再次发起连接。由一次短暂丢包引起的推断,可能留下比丢包本身更久的结果。
连续探测和阈值可以提高故障的可能性,但其真正含义仍是本地风险选择:当缺少证据持续到某一程度,继续保留不确定性已经比关闭更昂贵。阈值不会把“没有看到”变成“亲眼看到死亡”。
用户超时回答的是另一道题
TCP 原本就有针对未确认数据的用户超时概念。RFC 5482 后来定义了传递超时偏好的选项。它问的是“已经发出的数据可以多久不被确认”,而不是“一个空闲对端多久没有说话”。
区分两种时钟能避免策略互相覆盖。RFC 5482 指出,某些 keepalive 策略会中止本可熬过临时中断的连接;按该规范同时使用时,keepalive 定时必须长于已采用的用户超时。业务数据、空闲探测和关闭决定相关,却不是同一条证据链。
中间设备也开始遗忘
端到端模型把连接状态交给两端,但 NAT 与有状态中间设备在路径里增加了另一套会过期的账本。两台主机仍保存一条有效连接,中间设备却可能已经删除下一包所需的映射。
RFC 5382 把两小时默认值变成 NAT 兼容边界:如果 NAT 无法判断已建立连接的端点是否活跃,就不得在两小时四分钟之前丢弃映射,多出的四分钟用于容纳在途报文。
这一规则保护端点预期,却没有赋予 NAT 定义连接生死的权力,也不能保证现实设备都遵守。面对更短的实际状态寿命,运营者往往缩短探测周期。这样做或许保住映射,却是在缴纳“中间设备税”:端点制造流量,并非应用需要通信,而是为了不让路径上的私有设备忘记自己。
电池呈现相反账单
对接入市电的服务器,多几个小包看起来微不足道。对节能设备,每次发送都可能唤醒无线电,并延长高功耗活动窗口。RFC 9006 准确记录了冲突:TCP 的长默认周期未必能维持某些中间设备状态,而更频繁的 keepalive 又可能消耗电池。
没有一个传输层常数能完成这项分配。应用知道延迟重连是否可接受,运营者知道路径状态寿命,设备知道自己的能量预算。有时最合理的决定甚至不是“保活”,而是让连接自然消失,在确有工作时重新建立。
能听见内核,却听不见应用
把 keepalive 称作心跳容易夸大它的听诊范围。内核可以照常 ACK,而服务进程已经死锁;代理可以回应 TCP,而上游依赖已经故障;远端应用也可能健康,只是被有意挂起。
应用层心跳能提出更强、也更具体的问题:服务能否解析请求、读取必要状态并给出有效响应?这种检查代价更高,也仍不能保证未来每项工作成功。两种机制只有在故障目标清晰时才应该并用。
Keepalive 也不是零窗口探测。零窗口探测防止接收方重新开放窗口的通知丢失,前提是对端明确宣告没有接收能力;keepalive 面对的是本来就没有业务流量的空闲连接。相似的小包维护的是不同契约。
沉默仍然享有无罪推定
Keepalive 最持久的设计,并不是 SND.NXT-1 这一技巧,而是拒绝让传输实现默认把沉默判为有罪。
收到 ACK,证明近期存在传输层响应;它不证明应用健康。没有 ACK,表示不确定;它不等于死亡。只有当应用明确选择一个风险预算,连续缺失才足以支持本地关闭决定。
承担错误后果的一端必须保有决定权,所以 keepalive 始终可选。TCP 提供了询问的方法,却没有声称自己知道对方为何没有回答。
来源与证据边界
RFC 793 奠定原始连接与用户超时机制;RFC 1122 记录 1989 年 keepalive 契约;RFC 5382 给出 NAT 时间要求;RFC 5482 区分用户超时与 keepalive 中止策略;RFC 9006 说明能源权衡;RFC 9293 汇总当前规则。这些材料不能证明每个平台的默认值、每台中间设备的状态时间,或每个应用的健康语义。
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
