要让swoole协程真正跑起来且不卡死,必须启用swoole_hook_all协程化、设置deadlock_check防静默卡死、验证协程环境、使用原生协程mysql/redis客户端,缺一不可。

要让Swoole协程真正跑起来且不卡死,不能只装扩展、写个go()就完事——必须在正确时机启用协程化运行时、设置调度检测阈值、验证当前执行上下文是否处于协程中,否则空循环会无声吞噬资源,请求堆积却无报错。
启用协程运行时(全局I/O协程化)
这一步决定你写的file_get_contents、curl_exec、sleep等调用会不会阻塞整个Worker进程。
在Swoole服务启动前、或Co::run()入口函数最开头,执行:
【Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL)】
不要只勾选SWOOLE_HOOK_CURL或SWOOLE_HOOK_FILE——漏掉任意一类未hook的同步调用,协程就会在那行代码上彻底挂起。SWOOLE_HOOK_ALL是唯一能覆盖DNS解析、文件读写、Socket连接、CURL、标准输入输出等全部系统调用的选项。
若用ThinkPHP + think-swoole,该调用需放在swoole.php配置文件的onStart回调里,或在app/Providers/AppServiceProvider.php的boot()中提前触发。
开启死循环检测(防协程静默卡死)
协程里写while(true) { }不会报错、CPU不飙升、top看不到异常,但request_count停滞、tasking_num锁死为0——这是最隐蔽的生产事故。
方法一:在Co::run()内启用
直接在协程主入口设检测参数:
Co::set([
'deadlock_check' => true,
'deadlock_timeout' => 3000
]);
方法二:在HTTP Server的workerStart中启用
确保每个新Worker进程都继承该配置:
$server->on('workerStart', function ($server, $worker_id) {
Swoole\Coroutine::set([
'deadlock_check' => true,
'deadlock_timeout' => 3000
]);
});
【deadlock_timeout建议设为2000~5000毫秒】:低于2000易误报密集计算,高于5000失去预警意义。
注意:该配置仅对当前Co::run()或当前Worker生命周期生效,Server模式下必须在onWorkerStart中重复设置,否则新fork的Worker进程不生效。
验证当前是否处于协程环境
很多协程函数(如Swoole\Coroutine\MySQL::query)要求必须在协程内调用,否则直接Fatal error。别靠猜,用代码确认。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
第一步:检查协程ID是否有效
var_dump(Swoole\Coroutine::getCid() !== -1); // 返回true才表示已在协程中
第二步:验证go()是否可用且调度正常
go(function () {
co::sleep(0.1);
echo "协程已调度\n";
});
echo "主线程继续\n";
第三步:查看协程统计信息
print_r(swoole_coroutine_stats());
重点关注coroutine_num(当前活跃数)和coroutine_peak_num(峰值)——如果两者差值持续扩大且不回落,说明协程没被正确回收或存在阻塞点。
确认协程MySQL与Redis客户端可用
数据库和缓存操作是协程卡死重灾区,光启runtime不够,客户端本身也得是协程版。
方法一:用Swoole原生协程MySQL
$mysql = new Swoole\Coroutine\MySQL();
$mysql->connect(['host' => '127.0.0.1', 'user' => 'root']);
方法二:ThinkPHP中改database.php
'type' => 'mysql', // 不是'pdo'
'deploy' => 0, // 关闭读写分离
'hostname' => '127.0.0.1', // 禁用getenv()
方法三:Redis必须用Swoole\Coroutine\Redis
$redis = new Swoole\Coroutine\Redis();
$redis->connect('127.0.0.1', 6379);
切记:传统phpredis扩展或Predis在协程中调用会直接阻塞,哪怕已启用SWOOLE_HOOK_ALL也无法协程化其底层socket操作。










