max_coroutine超限会立即报错并断连,不排队不降级;错误为“PHP Warning: Swoole\Coroutine::create(): maximum coroutine count exceeded”,连接被强制关闭,客户端收RST或空响应。

max_coroutine 不是“并发上限”,而是单个 Worker 进程内协程数量的硬性内存闸门;超限直接报错、断连,不是排队或降级。
max_coroutine 超限会触发什么错误
当当前 Worker 进程中活跃协程数达到 max_coroutine 设置值时,后续调用 go() 或协程化 I/O(如 curl_exec()、mysql_query())将立即失败,底层抛出类似以下错误:
PHP Warning: Swoole\Coroutine::create(): maximum coroutine count exceeded in ...
此时该连接会被强制关闭,客户端收到 RST 或空响应。它不会等待、不进队列、不 fallback 到同步模式。
- 错误发生后,
swoole_get_status()中的coroutine_num会卡在上限附近,coroutine_peak_num持续等于max_coroutine - 常见误判是“QPS 上不去”,实则是协程创建失败导致请求被静默丢弃
- 若日志里没看到该警告,但有大量超时或空响应,要检查是否
error_log级别屏蔽了E_USER_WARNING
为什么默认值 65536 反而容易出问题
新版 Swoole(≥4.5)默认 max_coroutine = 65536,但这个数字脱离业务场景就是陷阱:
- 每个协程默认占
coroutine_stack_size = 256K(注意:不是 8KB!那是旧版或自定义栈大小时的估算),65536 × 256K ≈ 16GB 内存 —— 单 Worker 就可能吃光整机内存 - 实际压测中,
max_coroutine=5000在 8 核机器上已能跑出 22K QPS,再往上 QPS 增益极小,延迟却明显上升 - 很多项目沿用默认值,又没监控
coroutine_peak_num,上线后某次活动流量突增,瞬间打满协程池,服务雪崩
建议起步配置统一设为 'max_coroutine' => 3000,再根据 swoole_get_status()['coroutine_peak_num'] 的压测峰值逐步上调,而非盲目拉高。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
max_coroutine 和 ulimit、memory_limit 的三角关系
它不是孤立参数,必须和系统限制联动校验:
-
max_coroutine不能超过系统ulimit -n(文件描述符上限)的 70%,否则协程内新建 socket 会失败 —— 因为每个协程可能持有一个或多个 fd - 总内存消耗 ≈
worker_num × max_coroutine × coroutine_stack_size,必须小于 PHP 的memory_limit,否则 Worker 进程启动即 OOM - 若开启
task_worker_num > 0,task 进程也有自己的max_coroutine(需单独设),且 task 进程的栈空间也计入整体内存
上线前务必跑一次校验脚本:ulimit -n 输出值、php -r "echo ini_get('memory_limit');"、以及你配置的三者乘积,三者缺一不可对齐。
max_coroutine 调大 ≠ 并发能力提升
这是最常被忽略的逻辑陷阱:协程密度不等于有效吞吐。
- 协程本身轻量,但密集创建/销毁带来调度开销;实测显示
max_coroutine从 3000 升到 8000,QPS 仅 +5%,P99 延迟却 +11ms - 真正卡瓶颈的往往是 MySQL 连接池大小、Redis 连接数、DNS 解析未协程化,而不是协程数量不够
- 如果你的代码里还存在
sleep()、usleep()或未设 timeout 的curl_exec(),哪怕max_coroutine=100000也救不了 —— 那些协程只是挂着不动,占坑不干活
调参前先确认 Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL) 已启用,并用 swoole_coroutine_stats() 查看是否有长期阻塞的协程。否则,所有 max_coroutine 的调整都是在给假瓶颈输血。

















