Workerman高并发CPU飙升主因是Worker进程陷入高频轮询、同步阻塞调用或未释放协程任务,需通过top -H定位高占用线程、ps -T匹配Worker PID、status查Busy状态,并排查sleep/file_get_contents等阻塞函数,替换为异步调用,启用workerman/mysql连接池,用taskset绑定CPU核心,禁用var_dump等调试输出。

Workerman在高并发场景下CPU使用率突然飙升,往往不是因为连接数太多,而是某个Worker进程陷入高频轮询、同步阻塞调用或未释放的协程任务,导致事件循环卡死或持续占用单核资源。
定位是哪个Worker进程在拖垮CPU
第一步:执行 top -H -p $(pgrep -f "php start.php"),按Shift+P按CPU使用率倒序,找到占用率异常高的线程PID。
第二步:用 ps -o pid,tid,comm -T -p [主进程PID] 查出该线程所属的Worker子进程PID(注意:Workerman每个Worker是一个独立PHP进程,其内部线程ID对应一个event loop实例)。
第三步:结合 php start.php status 输出,比对PID与“Busy”状态栏——若某进程长期显示Busy且内存持续上涨,基本可判定它正执行同步IO或死循环。
检查是否用了阻塞式函数
Workerman所有回调(onMessage/onConnect/onClose等)必须运行在非阻塞上下文中,【sleep()、file_get_contents()、curl_exec()、PDO::query()这些函数会直接卡住整个Worker进程的事件循环】。
方法一:全局搜索代码中出现的sleep(、usleep(、file_get_contents(,替换成\Workerman\Timer::add(0.01, ...)或协程await调用。
方法二:若使用Workerman 5.0+,启用协程后仍出现CPU飙升,检查是否混用了Swoole扩展的Swoole\Coroutine\run()——Workerman原生协程与Swoole协程不兼容,共存会导致调度混乱。
启用异步数据库与HTTP客户端
同步MySQL查询是CPU飙升最常见元凶。必须替换掉PDO或mysqli直连方式。
安装协程MySQL驱动:composer require workerman/mysql,然后在Worker启动时初始化连接池:
$db = new Workerman\Mysql\Connection('127.0.0.1', 3306, 'user', 'pass', 'db');
在onMessage中改用await查询:$rows = await $db->select('*')->from('log')->where(['status' => 0])->query();
【切勿在协程回调里new PDO()或mysqli_connect()——每次新建连接都触发同步DNS解析和TCP握手,瞬间拉满CPU】。
强制绑定CPU核心防调度抖动
Linux内核默认调度可能把全部Worker挤到同一物理核心,尤其在4核以下机器上极易出现单核100%而其余核心空闲。
写一个shell启动脚本start_affinity.sh:
#!/bin/bash
CORES=(0 1 2 3)
for i in "${!CORES[@]}"; do
taskset -c "${CORES[$i]}" php start.php start -d --process-name="worker-$i" &
done
这一步必须做——Workerman自身不提供cpu_affinity配置项,【仅靠修改Worker::$processCount或加注释无法实现亲和性绑定】。
关闭不必要的调试与日志输出
开发期常用的var_dump()、print_r()、error_log()在高并发下会引发频繁系统调用和缓冲区刷盘,实测可使CPU负载额外增加15%~30%。
将日志切换为异步写入模式:用Workerman\Worker::setLogPath('/tmp/workerman.log')并确保磁盘I/O不瓶颈;更推荐接入monolog + workerman/async-log组件。
上线前务必删除所有echo、var_dump、debug_backtrace()调用——它们不会报错,但会让Worker在每秒万级请求下反复分配字符串内存并触发GC。

















