长连接为什么被静默回收
长连接「闲置一段时间后第一个请求失败、之后又恢复正常」是典型的会话被中间设备回收:NAT 网关、防火墙、负载均衡都维护会话表,空闲超过阈值就删除表项,两端应用却不知道,直到下次发包才发现对端已无状态。解法是让保活流量比中间设备的空闲超时更频繁,把这个阈值摸清楚是关键。应用层心跳最可靠,因为内核 keepalive 只对 TCP 层可见,且默认间隔长达两小时。
基本信息
| 内核默认 | net.ipv4.tcp_keepalive_time 为 7200 秒,远大于常见的 300 至 900 秒会话超时 |
|---|---|
| 应用侧 | 心跳间隔取中间设备超时的一半以内,例:超时 350 秒则心跳 120 秒 |
| 内核参数 | tcp_keepalive_time、tcp_keepalive_intvl、tcp_keepalive_probes |
| 观测命令 | ss -tino 看 keepalive 定时器与重传计数 |
典型使用场景
- 消息推送长连接夜间掉线:核对心跳与 NAT 空闲超时的关系
- 数据库连接池出现陈旧连接:池内存活检测要覆盖中间设备超时
- 云负载均衡后长连接被断开:调整 LB 空闲超时或应用心跳
- 移动网络下连接频繁失效:运营商侧会话回收时间更短
- 调整内核保活参数:让内核探测与应用心跳互相配合
现状核对与设置
现状核对与设置
ss -tino state established '( dport = :443 )' | head # 看 keepalive 定时器
sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_intvl net.ipv4.tcp_keepalive_probes
# 建议:应用层心跳小于中间设备空闲超时的一半
# 例:NAT 超时 350 秒 → 心跳 120 秒
sysctl -w net.ipv4.tcp_keepalive_time=120 # 临时验证,重启失效
# 永久生效写入 /etc/sysctl.d/ 下的配置文件后再 sysctl --system现象与归因
| 现象 | 可能原因 | 验证方式 |
|---|---|---|
| 闲置后首个请求超时 | NAT 或防火墙回收会话 | 两侧抓包看首个包是否有 RST |
| 连接被对端复位 | LB 空闲超时主动断开 | 查 LB 会话日志与超时配置 |
| 心跳无响应但连接仍显示 ESTAB | 中间设备单向丢包 | 两端对照抓包比对心跳 |
| 夜间固定时段掉线 | 策略设备定时清理会话 | 核对策略设备的空闲老化时间 |
单向心跳不够:部分设备只统计双向流量或只放行已建会话的回包,建议由一端发起且能收到响应,才算真正刷新了中间设备的状态。