workerman并发上不去的根本原因是onmessage中存在同步阻塞操作,如file_get_contents、sleep等,导致单线程事件循环停摆,所有连接排队等待;必须改用异步http客户端、异步数据库驱动及非阻塞io实现。

Workerman 本身不解决多用户同时发送数据时的网络 IO 阻塞——它只提供非阻塞 IO 基础,真正卡住你的,几乎总是 onMessage 里写的同步代码。
为什么 onMessage 一写 file_get_contents 就卡住所有连接
Workerman 的事件循环是单线程的(每个 Worker 进程一个),一旦某个 onMessage 回调执行了同步阻塞操作(比如 file_get_contents、sleep、未设超时的 curl_exec、PDO 查询),整个进程的事件循环就停摆。后续所有用户的请求都排队等待,看起来像“并发上不去”,其实是被自己拖死的。
- 现象:压测时 RT 持续升高、QPS 不增反降、
top显示 CPU 很低但连接堆积 - 根本原因:PHP 原生函数默认都是同步阻塞的,和 Workerman 的异步模型天然冲突
- 验证方式:在
onMessage开头加echo date('H:i:s') . " start\n";,结尾加echo date('H:i:s') . " end\n";,看日志时间戳是否连续跳变
用 event 扩展 + 异步客户端才能绕过系统级 IO 阻塞
装 event 扩展只是让 Workerman 能用 epoll 监听成千上万个 socket,但它不改变 PHP 函数本身的阻塞行为。真正要解 IO 阻塞,得换掉那些“等结果”的调用。
- HTTP 请求:必须换成
Swoole\Coroutine\Http\Client(需 Swoole)或ReactPHP/Amp的异步 HTTP 客户端;禁用file_get_contents和curl_exec - 数据库:不能用
PDO或mysqli,得用amphp/mysql或swoole_mysql这类原生支持协程/异步的驱动 - 文件读写:避免
file_get_contents/file_put_contents;大文件走stream_select+ 非阻塞流,小日志走Monolog的BufferHandler或 UDP 发送
暂停 accept 是防“雪崩式排队”,不是治本方案
有些场景下,你没法立刻改异步(比如 legacy 代码),可以用 $worker->pauseAccept() + $worker->resumeAccept() 控制连接接入节奏,防止新连接疯狂涌进来把队列打满。但这只是缓解,不是消除阻塞。
- 适用场景:短时突发流量、调试阶段快速验证阻塞点
- 注意点:必须在
onConnect里pauseAccept,在onClose或onMessage结束后resumeAccept,否则连接永远进不来 - 风险:如果
onMessage卡死没触发resumeAccept,整个 Worker 就彻底拒绝新连接
真正的高并发稳定运行,不靠技巧性暂停,而靠把所有可能阻塞的环节替换成异步实现——哪怕只漏掉一个 sleep(1),整个 Worker 进程就退化成 Apache 模式。











