linux内核tcp keepalive默认7200秒首探,无法应对nat设备10–15分钟清理策略;必须调为tcp_keepalive_time=60、intvl=30、probes=3,并在swoole/hyperf中同步配置tcp_keepidle=60、keepinterval=30、keepcount=3,且客户端需协同适配。

Hyperf 单机跑长连接,TCP Keepalive 不配好,12 分钟左右就断,不是代码问题,是中间网络设备(比如运营商 NAT、家用路由器)主动踢掉空闲连接。关键不在应用层心跳,而在操作系统和 Swoole 层的 TCP 底层保活机制。
Linux 内核级 TCP Keepalive 必须调短
默认 2 小时才探测一次(tcp_keepalive_time=7200),完全扛不住 NAT 设备 10–15 分钟的清理策略。必须主动压低:
- tcp_keepalive_time=60:空闲 60 秒后开始探测
- tcp_keepalive_intvl=30:每 30 秒发一次 ACK 探测包
- tcp_keepalive_probes=3:连续 3 次无响应即断连
执行命令:sysctl -w net.ipv4.tcp_keepalive_time=60 等,并写入 /etc/sysctl.conf 持久化。
Swoole 服务端需显式启用并设参
Hyperf 基于 Swoole,仅靠内核参数不够,Swoole 还要自己开 keepalive 并设合理间隔:
- 在
config/autoload/server.php的 server 配置中加:'open_tcp_keepalive' => true - 配合设置:
'tcp_keepidle' => 60(首次探测前空闲秒数) -
'tcp_keepinterval' => 30(后续探测间隔) -
'tcp_keepcount' => 3(最大探测失败次数)
注意:这些值应与内核参数对齐,否则 Swoole 可能忽略或覆盖系统行为。
Hyperf 连接池里的 idle 检查不能替代 TCP Keepalive
很多人误以为配了 max_idle_time 和 heartbeat 就能保活长连接——其实那是数据库/Redis 连接池的“连接有效性检查”,只管取出前是否可用,不解决传输层连接被中间设备静默回收的问题。
- TCP Keepalive 是 OS 层发纯 ACK 包,穿透所有中间设备
- 连接池的 heartbeat 是业务层 PING,走的是应用协议(如 SELECT 1),依赖连接还通着才能发出去
- 两者职责不同,必须同时配,不能互相替代
客户端也要同步配合
服务端调好了,客户端没跟上,照样断:
- 移动端 SDK 或浏览器默认 keep-alive 超时通常较短(iOS NSURLSession 默认约 60 秒),需确认其底层 socket 是否启用了 keepalive
- 若用 curl 测试,加
-H "Connection: keep-alive"并避免设置过短的--max-time - WebSocket 客户端(如 JS 的
WebSocket对象)本身不暴露 keepalive 控制,靠底层浏览器 TCP 栈,所以更依赖服务端和系统参数
不复杂但容易忽略











