协程雪崩的本质是资源耗尽而非代码错误,表现为大量协程卡在I/O等待、阻塞调用或死锁同步点,导致调度器积压、内存暴涨、CPU飙升,最终服务不可用。

协程雪崩的本质是资源耗尽而非代码错误
协程雪崩不是抛出异常,而是大量协程卡在 I/O 等待、阻塞调用或死锁同步点上,导致调度器积压、内存暴涨、CPU 占用飙升,最终服务不可用。它常发生在未设限的并发场景下,比如循环中无节制调用 go() 或 Coroutine::create()。
必须设置 swoole.max_coroutine 且不能只靠默认值
默认值(通常为 3000)在高负载下极易突破,尤其当每个协程都持有连接、缓存或 Channel 句柄时,实际可用协程数远低于理论值。不设限等于放任内存和文件描述符被快速吃光。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
-
swoole.max_coroutine应按单机物理内存和业务协程平均开销反推:例如 4GB 内存、单协程均摊 2MB,则上限建议设为1500左右 - 该配置需在
php.ini中静态设置,运行时ini_set()无效 - 配合
swoole.enable_coroutine=On才生效,缺一不可 - 上线前务必用
strace -e trace=clone,brk,mmap观察协程创建行为,确认是否真被限制
用 Semaphore 控制关键资源并发数,比 WaitGroup 更防雪崩
WaitGroup 只解决“等全部完成”,不阻止新协程启动;而 Semaphore 是硬闸门,能直接掐断超额请求。数据库查询、HTTP 调用、Redis 操作这类外部依赖,必须套一层信号量。
- 初始化:$sem = new
Swoole\Coroutine\Semaphore(5)表示最多 5 个并发执行 - 协程内必须先
$sem->acquire(),再做耗时操作,结束后立即$sem->release() - 不要在
acquire()前做任何可能失败的初始化(如连接池取连接),否则可能永久占用信号量 - 超时控制要写在
acquire()参数里:$sem->acquire(3)表示最多等 3 秒,超时返回false,避免协程无限挂起
协程内异常未捕获会静默终止,但资源不会自动释放
这是最隐蔽的雪崩诱因:一个协程因未 catch 的 Exception 或 throw 退出,其持有的 Channel、Socket、MySQL 连接句柄可能未 close,持续占用底层资源。调度器仍会尝试唤醒它(直到超时或被强制回收),形成“僵尸协程”。
- 所有
go()包裹的匿名函数,开头必须加try { ... } catch (\Throwable $e) { Swoole\Coroutine::stats()->incr('coroutine_error'); } - 禁止在协程中使用
exit或die,它们会直接终止整个进程 - 用
Swoole\Coroutine::stats()定期检查coroutine_num和coroutine_peak_num,若峰值持续接近max_coroutine,说明有泄漏或阻塞 - 生产环境开启
swoole.display_errors=On并重定向到日志,否则协程内错误根本看不到
coroutine_peak_num 和系统 ulimit -n 的差值,比看 QPS 更早暴露风险。

















