TCP Keep-Alive 机制是什么?为什么还需要应用层心跳?
TCP Keep-Alive 是操作系统提供的连接存活检测机制:连接空闲一段时间后,内核自动发探测包,根据对端响应判断连接是否还活着。三个核心参数控制行为——空闲多久开始探测(tcp_keepalive_time,默认 7200 秒)、探测间隔(tcp_keepalive_intvl,默认 75 秒)、探测几次放弃(tcp_keepalive_probes,默认 9 次)。最差情况下,从连接断开到被检测出来要 7200 + 75×9 = 7875 秒,超过 2 小时。
追问
为什么默认 2 小时这么长?
RFC 1122 建议至少 2 小时,是出于对网络风暴的担忧——如果全网所有连接都以短间隔发探测包,本身就是一场 DDoS。2 小时在服务器间稳定网络里够用了,问题出在移动端:运营商 NAT 设备的连接跟踪表有限,空闲 5 分钟(移动 2/3G)到 28 分钟(电信 3G)就淘汰条目,连接就被静默丢弃了,2 小时探测根本来不及救。
既然有 Keep-Alive,为什么还要应用层心跳?
三个原因。第一,Keep-Alive 只能检测连接是否可达,不能检测对端进程是否卡死——进程死锁时 TCP 连接还活着,Keep-Alive 照样通过。第二,Keep-Alive 的探测包不带业务数据,服务端对探测无感知,无法在应用层做状态同步。第三,参数是系统级的,改了影响所有连接,不如应用层心跳可以按业务精细控制间隔。微信的心跳从 30 秒到 300 秒动态调整,Keep-Alive 做不到。
Keep-Alive 探测包长什么样?
一个不包含数据的 ACK 包,序列号设为对端期望的序列号减 1,这样对端会发现序列号不匹配,回一个 ACK 带上正确的期望序列号——探测就成功了。如果对端回复 RST,说明进程已崩溃重启;如果连续 9 次无响应,内核判定连接死亡,关闭 socket 并返回 ETIMEDOUT。
什么场景下 Keep-Alive 就够了?
服务器之间的稳定内网连接。比如微服务间 gRPC 长连接、数据库连接池,网络环境可控,不存在 NAT 超时问题,Keep-Alive 配合较短的探测间隔(比如 60 秒)就能及时清理僵死连接。这些场景不需要应用层心跳的灵活性。
写段代码
pythonimport socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) # 60秒后开始探测 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) # 每10秒探测一次 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3) # 探测3次放弃