swoole协程无全局并发数限制开关,需结合max_coroutine配置与业务层channel/信号量控制;max_coroutine是硬上限而非控制器,超限直接报错中断服务,实际并发须通过channel缓冲池或waitgroup等机制优雅限流。

直接结论:Swoole 协程本身没有全局“并发数限制”开关,必须靠 max_coroutine 配置 + 业务层信号量/通道控制,否则协程会无节制创建,直到耗尽内存或触发系统级限制。
为什么不能只靠 max_coroutine 控制并发数
max_coroutine 是底层硬限制,不是并发控制器。它表示当前 worker 进程最多能同时存在多少个活跃协程;一旦超限,Swoole\Coroutine::create() 或 go() 会直接失败并抛出 Fatal error: unable to create coroutine,连接被强制关闭——这不是优雅降级,而是服务中断。
常见误用场景包括:在 HTTP 请求回调里无节制 go() 调用数据库或 API,QPS 上升后瞬间打爆 max_coroutine,错误日志里反复出现 coroutine stack size exhausted 或 unable to create coroutine。
-
max_coroutine默认值是3000,但实际可用数受stack_size和物理内存制约(比如设为2 * 1024 * 1024,3000 个协程就占约 6GB 栈内存) - 它不区分“正在运行”和“已挂起等待 IO”的协程,只要没结束就算一个
- 多个 worker 进程各自独立计数,总上限是
worker_num * max_coroutine,但业务逻辑通常需要的是单请求内或全局队列级的并发压制
用 Channel 实现请求级并发限流
最常用、最轻量的方式是用有缓冲的 Channel 做“协程槽位池”。它天然支持阻塞式获取与释放,适合控制同一类任务的并发度(比如同时最多 3 个 HTTP 请求发出去)。
关键点:
- 缓冲区大小即最大并发数:
new Channel(3)表示最多 3 个协程能同时执行 - 每个协程启动前
$channel->push(1),完成时$channel->pop()(注意不是pop后再处理,而是先占位再干活) - 如果所有槽位被占满,后续
push()会阻塞,直到有协程pop()归还槽位——这实现了排队等待,而非直接失败 - 不要用无缓冲
Channel,否则容易因发送/接收不同步导致永久阻塞
示例片段:
use Swoole\Coroutine\Channel;
<p>$channel = new Channel(3); // 最多 3 并发</p><p>for ($i = 0; $i push(1); // 等待空闲槽位
defer(fn => $channel->pop()); // 保证退出时归还</p><pre class="brush:php;toolbar:false;"> // 实际任务,比如调用 API
$client = new Co\Http\Client('api.example.com', 443);
$client->set(['timeout' => 5]);
$client->get('/data?id=' . $i);
echo "done {$i}\n";
});}
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
用 WaitGroup + add() 控制固定数量子协程
当你明确知道要启动 N 个协程并等它们全部完成(比如批量查库、聚合数据),WaitGroup 比 Channel 更直接。但它不提供排队或限流能力,只做同步等待。
致命陷阱:
-
add()必须在go()之前调用,否则可能主协程已执行wait()而子协程还没add,导致永远卡住 -
done()必须配defer,否则协程异常退出时不会调用,wait()就再也收不到信号 -
add()和done()次数必须严格相等,多一次或少一次都会让wait()失效或报错negative counter
正确写法:
use Swoole\Coroutine\WaitGroup;
<p>$wg = new WaitGroup();
$wg->add(5); // 明确要等 5 个</p><p>for ($i = 0; $i $wg->done()); // 异常也确保 done</p><pre class="brush:php;toolbar:false;"> Co::sleep(0.1);
echo "task {$i} done\n";
});}
$wg->wait(); // 主协程阻塞至此
真正容易被忽略的底层限制
很多人调高了 max_coroutine,却忘了三个更隐蔽的瓶颈:
- 系统文件描述符(FD)上限:每个协程可能持有一个 socket,
ulimit -n不够会导致Too many open files,和协程数无关但效果一样 -
stack_size设置过大(比如 >4MB),少量协程就吃光内存;过小( -
Swoole\Table或其他共享内存结构未显式destroy(),重启后残留内存挤占可用空间,间接压缩协程可用内存
检查当前真实协程负载,别只看配置:运行中执行 Co::stats(),关注 coroutine_num 和 coroutine_peak,后者才是你实际压测达到的峰值。










