swoole处理大规模长连接心跳依靠「服务端被动检测+应用层主动探测」双机制,非客户端轮询或仅依赖tcp keepalive;需合理配置heartbeat_check_interval(建议为heartbeat_idle_time的1/3~1/2)和heartbeat_idle_time(须大于业务最大静默间隔,如llm流式响应间隙),并协同内核keepalive与闭环应用层心跳帧实现高可靠性。

直接说结论:Swoole 处理大规模长连接心跳,靠的是「服务端被动检测 + 应用层主动探测」双机制,不是靠客户端 ping/pong 轮询,更不能只依赖 TCP keepalive。
heartbeat_check_interval 和 heartbeat_idle_time 怎么配才不翻车
这两个参数是 Swoole 内置心跳的全部控制开关,但配错会批量 kill 连接或漏判断连。
-
heartbeat_idle_time必须大于业务最大静默间隔(比如 LLM 流式响应间隙可能达 45 秒),否则没发完 token 就被关了 -
heartbeat_check_interval建议设为heartbeat_idle_time的 1/3~1/2,比如 idle 设 60 秒,check 设 20~30 秒;设太小(如 5 秒)会导致每秒扫描数万连接,CPU 毛刺明显 - 不要把
heartbeat_idle_time设成 0 —— 这会禁用心跳,所有死连接只能靠 TCP 四次挥手或超时重传才能释放 - 实测中,
heartbeat_idle_time = 300(5 分钟)、heartbeat_check_interval = 60是 LLM 类长连接较稳的起点
为什么光靠 Swoole 内置心跳不够用
内置心跳只看“最后收包时间戳”,属于单向、后验判断,对 NAT、防火墙、移动网络切后台等场景完全失效。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 客户端进后台、Wi-Fi 切蜂窝、路由器开启 ALG,连接在中间设备已断,但 Swoole 还以为它活着
- 客户端进程崩溃但 TCP 连接未发 FIN,服务端要等满
heartbeat_idle_time才关,期间 fd 占着、内存不释放 - 没有 pong 回执,无法区分“包丢了”还是“人死了”,容易误杀正在传输大 response 的连接
- 某些云厂商(如早期腾讯云 LB)会静默丢弃空闲超 90 秒的连接,而内核
tcp_keepalive_time默认是 7200 秒,根本对不上
应用层心跳怎么写才扛得住万级并发
不是简单加个 swoole_timer_tick 发 ping,得结合协程上下文、连接状态、错误重试做闭环。
- 客户端心跳必须带唯一
seq_id,服务端收到后立即$server->push($fd, json_encode(['type'=>'pong', 'seq'=>$seq_id])),避免乱序或重放 - 服务端 onMessage 中收到 ping 后,仅更新
$server->connections[$fd]['last_heartbeat'] = time(),别做任何阻塞操作 - 心跳超时清理不能只靠 onClose —— 要在 task worker 里异步查
last_heartbeat,再调$server->close($fd),防止阻塞 eventloop - 别用
sleep()或同步 Redis 查询做心跳逻辑,协程一堵,整条连接线就卡死 - 示例片段(服务端 onMessage):
if ($data['type'] === 'ping') { $server->connections[$fd]['last_heartbeat'] = time(); $server->push($fd, json_encode(['type' => 'pong', 'seq' => $data['seq'] ?? 0])); }
内核 TCP keepalive 和 Swoole 心跳要不要一起开
要,但必须对齐时间,否则互相打架。
- Linux 内核默认
tcp_keepalive_time=7200,远大于业务需要,必须调低:echo 300 > /proc/sys/net/ipv4/tcp_keepalive_time echo 30 > /proc/sys/net/ipv4/tcp_keepalive_intvl echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
- 内核 keepalive 是保底层链路,Swoole 心跳是保业务语义,两者触发条件不同:前者在 socket 层无数据时发 ACK 探测,后者在应用层无消息时删 fd
- 如果只开内核 keepalive,Swoole 不知道连接已被 kernel 关闭,
onClose可能延迟数分钟才触发 - 如果只开 Swoole 心跳,遇到中间设备静默丢包,
onClose根本不触发,fd 泄漏
真正难的不是配参数,而是当 onClose 触发时,你有没有在 10ms 内完成上下文清理、Redis 订阅退订、数据库连接归还 —— 这些动作一旦慢,fd 就卡在 closing 状态,新连接进不来。










