跳转到主内容
极星编程网:以代码为星,赴技术山海!

为什么在高并发下Workerman的onClose回调没有执行?

onClose未执行主因是连接异常断开(如掉电、NAT超时)致FIN包未送达,Workerman无法感知;高并发下死链堆积更易耗尽资源,须依赖应用层心跳(如每30秒检测lastReceiveTime超60秒则close)主动清理。
onClose
回调在高并发下“没执行”,大概率不是它被跳过了,而是连接根本没被正常断开 —— Workerman 无法感知,自然不会触发。 这背后是 TCP 协议层和应用层的错位:Workerman 只能靠对端发 FIN 包、或本端
close()
调用来判断连接结束。一旦硬件掉电、网线拔掉、NAT 超时静默回收连接,FIN 就永远不会来,
onClose
就永远等不到。 为什么高并发时这个问题更明显? 并发量一大,连接数多,系统资源(如 TIME_WAIT 状态数、文件描述符)吃紧,NAT 设备/防火墙/运营商网关更容易提前丢弃“空闲但未 FIN”的连接;而 Workerman 还在等那个永远不会来的 FIN。 netstat 显示 ESTABLISHED,但客户端早已失联 用
netstat -anp | grep :端口
查看,大量连接卡在
ESTABLISHED
,但客户端实际已断网或重启 这类“死链”会持续占用
TcpConnection
实例、内存、fd,最终拖慢整个事件循环,甚至导致新连接拒绝
onClose
不会触发,因为内核没通知,Workerman 无从得知 不依赖 FIN 的主动探测方案 必须自己做心跳,不能指望 TCP keepalive(默认 2 小时才探活,太迟): Workerman 最新主版本,继续强化高性能异步网络能力,适合 WebSocket、HTTP 服务、长连接、IM、IoT 等场景。 本次版本主要围绕稳定性、协议兼容以及连接处理能力优化,依旧保持超轻量和纯 PHP 特性。 适用于 PHP 8.x 环境,可配合 webman、GatewayWorker 等生态使用。 下载 在
$worker->onMessage
里记录每个
$connection->lastReceiveTime = time()
用
Worker::addTimer(10, function() { ... })
每 10 秒遍历所有连接,对超过 60 秒没收到数据的调用
$connection->close()
若用 GatewayWorker,直接配
Gateway::$pingNotResponseLimit = 60
和
Gateway::$pingInterval = 30
,它会自动发
ping
并清理无响应连接 注意:不要在定时器里做耗时操作(如查 DB、写文件),否则阻塞事件循环,反而让心跳检测更不准 UDP 场景下 onClose 根本不生效 如果协议走的是 UDP(比如
udp://0.0.0.0:8080
),
onClose
回调压根不会注册,也不会触发 —— UDP 是无连接的,没有“断开”概念。此时只能靠业务层超时机制(如收到某 ID 数据后 30 秒没续命,就视为离线)。 真正难处理的,是那些既没发 FIN、也没发心跳、还卡在 ESTABLISHED 的连接。它们不声不响地吃资源,直到某次压力突增才暴露。心跳逻辑必须轻量、独立、可监控,别让它成为下一个瓶颈。

相关文章