必须确认 swoole 协程已启用且 runtime::enablecoroutine() 在入口第一行调用,并显式设置最小必要 hook 标志;file_put_contents 等普通文件操作不协程化,需改用 co::writefile 或 task_worker。

必须先确认 Swoole 协程支持已启用
没开协程支持,Runtime::enableCoroutine() 会静默失效。运行 php --ri swoole,输出里必须包含 Coroutine => enabled 和 hooks => enabled。如果显示 disabled,说明编译时漏了 --enable-coroutine,或 PECL 安装的版本太旧(如 4.8.0 之前默认不启 hook)。别跳过这步——很多“协程不生效”问题根源在此。
Runtime::enableCoroutine() 要放在最顶层、最早执行的位置
它不是“开关”,而是协程环境的初始化动作,必须在任何协程操作(比如 go())和 I/O 调用之前调用。常见错误写法:
- 放在
onRequest回调里 —— 每次请求才启用,但此时文件/DB/HTTP 等函数可能已被 PHP 解析器缓存为同步行为,hook 失效 - 放在
go()内部 —— 协程已启动,再调用无意义 - 放在 Composer autoloader 之后 —— 某些类的静态初始化可能已触发原生函数调用,错过 hook 时机
正确位置:入口文件(如 server.php)第一行,或 ThinkPHP 的 swoole.php 配置加载前。
<?php \Swoole\Runtime::enableCoroutine(); // 后续才能安全使用 file_get_contents()、PDO、Redis::connect() 等
hook_flags 参数不是可有可无的配置项
默认 SWOOLE_HOOK_ALL 看似省事,但实际容易踩坑:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 某些扩展(如 Xdebug、pcov)与部分 hook 冲突,导致进程崩溃或死锁
- MySQLi/PDO 的
SWOOLE_HOOK_STREAM会接管底层 socket,但若你混用原生fsockopen(),可能因 fd 复用出错 - 生产环境建议显式指定最小必要集,例如 Web 服务常用:
SWOOLE_HOOK_TCP | SWOOLE_HOOK_UDP | SWOOLE_HOOK_UNIX | SWOOLE_HOOK_SSL | SWOOLE_HOOK_TLS
ThinkPHP 6 的 swoole.php 中协程配置应这样写:
'coroutine' => [
'enable' => true,
'flags' => SWOOLE_HOOK_TCP | SWOOLE_HOOK_UDP | SWOOLE_HOOK_SSL
],
协程化后,file_put_contents() 这类操作仍可能阻塞
这是最容易被忽略的点:Swoole 的 hook 仅覆盖标准流(stream)、Socket、DNS、MySQLi/PDO、Redis 等,但不包括 fopen() 的普通文件句柄(php:// 除外)。所以:
-
file_get_contents('http://...')→ ✅ 自动协程化(走streamhook) -
file_get_contents('/tmp/data.txt')→ ❌ 仍是同步阻塞(除非用co::readFile()) -
file_put_contents('./log.txt', $data)→ ❌ 同步写,会卡住整个协程
解决方案只有两个:co::writeFile() / co::readFile(),或者把日志投递到 task_worker 异步处理。别指望 enableCoroutine() 能魔法般解决所有 IO。










