根源是内核在高并发连接断开时反复处理关闭事件;strace见密集epoll_pwait且返回0或极短等待,表明事件循环空转;全连接队列溢出、file-max/time_wait限制过低、onclose同步阻塞均会加剧sys cpu飙升。

strace 看见密集 epoll_pwait 就是根源
Workerman 进程在高并发连接断开时 CPU sys 占用飙升,90% 以上情况是内核在反复处理连接关闭事件,而不是业务代码问题。直接用 strace -tt -p PID 观察输出:如果每秒刷出几十行 epoll_pwait 且返回值是 0(超时)或毫秒级极短等待,说明事件循环在空转轮询——它本该挂起等待 I/O,却被迫高频唤醒。
net.core.somaxconn 和全连接队列溢出有关
连接断开本身不直接导致 sys 高,但大量短连接(尤其是建连后立刻断开)会快速填满内核全连接队列。队列满后,内核丢弃 ACK 包却不发 RST,Workerman 主线程仍持续调用 accept() 尝试取连接,结果总失败,陷入“调用 → 失败 → 再调用”循环,表现为 sys CPU 持续拉高。
- 查当前生效队列长度:
ss -lnt | grep :端口号,看Send-Q是否卡在默认的128 - 临时调大:
sudo sysctl -w net.core.somaxconn=65535 - 永久生效:写入
/etc/sysctl.conf并执行sysctl -p - Workerman 侧需显式指定 backlog(如
websocket://0.0.0.0:2346?backlog=65535),否则仍走系统默认值
file-max 和 TIME_WAIT 连锁反应不能忽略
大量连接断开会快速生成 TIME_WAIT 状态套接字,若 fs.file-max 或进程级 nofile 限制过低,系统会频繁回收和重用端口,触发内核更激进的连接状态管理逻辑,间接抬高 sys 开销。
- 检查当前限制:
cat /proc/sys/fs/file-max和ulimit -n - 建议设为
2097152(file-max)和1048576(nofile),并写入/etc/security/limits.conf - 启用
net.ipv4.tcp_tw_reuse = 1,允许对端处于 TIME_WAIT 的 socket 被复用于新连接(仅客户端主动发起时有效) - 注意:
tcp_tw_recycle已废弃,切勿启用
onClose 回调里别做同步阻塞操作
虽然 onClose 是连接终止时触发,但若在里面调用 file_put_contents、mysqli_query 或未 Hook 的 curl_exec,会导致 Worker 进程卡住。此时事件循环无法及时响应其他连接的关闭事件,内核积压的待处理关闭请求变多,进一步加剧 epoll_pwait 频次。
- 日志写入必须异步:用
Swoole\Coroutine\WriteFile或消息队列落盘 - 数据库清理类操作,改用 Task Worker 异步执行,不要留在
onClose同步跑 - 确认已调用
Swoole\Runtime::enableCoroutine(true)(在Worker::runAll()前),否则所有同步函数都会退化为阻塞
真正难排查的是那些没打日志、不报错、但让内核忙得团团转的配置失配——比如 somaxconn 没调,backlog 没显式传,file-max 卡在默认值,三者叠加,连接一抖就 sys 爆表。











