默认tcp_keepalive_time=7200不适用长连接,需设为300并确保应用层启用so_keepalive;ssh还需配置clientaliveinterval和tcpkeepalive;纯tcp探测不可靠,须叠加应用层心跳。

默认的 tcp_keepalive_time=7200(2小时)在大多数长连接场景下根本不够用,中间防火墙或NAT设备通常5–10分钟就回收空闲连接,不调必断。
为什么改了sysctl还是断连?先确认SO_KEEPALIVE是否真启用了
内核参数只是“探测行为”的开关,但前提是应用层 socket 显式启用了 SO_KEEPALIVE。很多服务(如 Nginx、PostgreSQL、自研 TCP 服务)默认不启用它,光调 tcp_keepalive_time 没用。
验证方法:
- 用
ss -ti查看某个连接:如果输出里有keepalive字样,说明已启用;没有则说明应用没开 - 检查服务文档或源码,确认是否调用了
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) - 某些语言 runtime(如 Go 的
net.Conn)默认不开,需手动设置SetKeepAlive(true)
三个参数怎么设才不踩坑
关键不是“越小越好”,而是要匹配你所处网络链路中最短的空闲超时阈值(通常是防火墙/NAT 设备的 300–600 秒)。设得太激进会增加无效探测包,设得太保守等于没设。
推荐组合(适用于云环境、企业 NAT、家用光猫等常见场景):
-
tcp_keepalive_time = 300:空闲 5 分钟后开始发第一个探测包(必须小于中间设备超时值) -
tcp_keepalive_intvl = 30:每次探测失败后,隔 30 秒再试(避免被当成扫描流量) -
tcp_keepalive_probes = 5:连续 5 次无响应才断连接(总探测窗口 = 300 + 5×30 = 450 秒 ≈ 7.5 分钟)
注意:tcp_keepalive_probes 为 0 表示禁用探测,设为 1 可能导致误杀(一次丢包就断),别设成 1。
临时改 vs 永久生效:别只做一半
用 sysctl -w 临时修改后,重启就失效——生产环境必须落盘。但直接往 /etc/sysctl.conf 追加容易重复,建议用 /etc/sysctl.d/ 独立文件管理:
- 创建
/etc/sysctl.d/99-tcp-keepalive.conf - 写入三行(等号前后有空格):
net.ipv4.tcp_keepalive_time = 300net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 5 - 执行
sudo sysctl --system加载全部配置(比sysctl -p更健壮)
验证是否生效:sysctl net.ipv4.tcp_keepalive_time,输出应为 300。
SSH 连接也断?别只调内核,还得配 sshd_config
SSH 是应用层协议,仅靠 TCP keepalive 不够稳。服务端若不主动探测,中间设备照样清连接。
必须同时配置 /etc/ssh/sshd_config:
-
TCPKeepAlive yes:启用底层 TCP 探测(和内核参数联动) -
ClientAliveInterval 60:服务端每 60 秒发一次 SSH 层心跳(SSH_MSG_GLOBAL_REQUEST) -
ClientAliveCountMax 3:连续 3 次无响应才 kill 连接(对应总空闲容忍≈3 分钟)
改完运行 sudo systemctl restart sshd。客户端连的时候加 -o ServerAliveInterval=30 可进一步加固,但服务端配置才是根本。
真正容易被忽略的是:TCP keepalive 探测包本身可能被中间设备静默丢弃(尤其某些企业防火墙),此时你看到连接“活着”,其实对端早已失联。这种情况下,必须叠加应用层心跳(比如 WebSocket ping/pong 或自定义 PING 帧),不能只信 TCP 层。











