最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
重点解析 Swoole 协程中的并发请求如何实现?
时间:2026-07-13 09:22:56 编辑:袖梨 来源:一聚教程网
协程并发请求无需手动 co::sleep,核心是调度器自动挂起/唤醒协程;只要使用协程友好的客户端(如 SwooleCoroutineHttpClient),直接 go 启动即可并行执行,$cli 实例须单协程内新建且不可复用。
协程并发请求必须用 go + co::sleep 或 I/O 等待?
不是。Swoole 协程并发的核心是「不阻塞」,不是靠 co::sleep 模拟。真正并发请求靠的是协程调度器自动挂起/唤醒,只要底层函数是协程友好的(比如 SwooleCoroutineHttpClient、co::curl_exec、Redis::connect),发起多个请求时无需手动 sleep,直接 go 启动即可并行执行。
常见错误是写成这样:
go(function () { $cli = new SwooleCoroutineHttpClient('httpbin.org', 443, true); $cli->get('/delay/1'); echo "done 1n";});go(function () { $cli = new SwooleCoroutineHttpClient('httpbin.org', 443, true); $cli->get('/delay/2'); echo "done 2n";});
这段代码实际耗时约 2 秒(非串行),因为两个协程在等待网络响应时被调度器自动让出 CPU,彼此不抢占——这才是并发本质。不需要加 co::sleep,加了反而拖慢。
SwooleCoroutineHttpClient 的 get/post 是同步阻塞还是协程挂起?
是协程挂起。调用 $cli->get() 时,如果底层 socket 尚未就绪,当前协程会立即让出控制权,调度器转去运行其他就绪协程;等 socket 可读时再唤醒该协程继续执行。整个过程对用户代码透明。
但要注意几个关键点:
-
$cli实例不能跨协程复用(每个go块里必须新建) - 必须在协程环境里创建和调用(比如
go内、onRequest回调里),否则报错ERRORswClient_send: send() failed, Error: Protocol not supported[93] - 超时设置要显式指定:
$cli->set(['timeout' => 5]),否则默认 0.5 秒,容易误判失败 - HTTPS 请求必须传
true第三个参数,且确保 OpenSSL 已启用,否则连接直接失败,错误信息是SSL_connect failed
为什么并发 100 个请求,实际只发出去 20 个?
大概率是系统级限制或 Swoole 配置没调优。Swoole 默认不限制协程数,但操作系统对单进程可打开文件描述符(fd)数量有限制(通常是 1024),而每个 SwooleCoroutineHttpClient 至少占用 1 个 fd(TCP 连接 + SSL 句柄可能更多)。
检查和调整方式:
- 运行
ulimit -n查看当前限制,临时提高:ulimit -n 65535 - 确认 Swoole 版本 ≥ 4.8.0,低版本存在 fd 复用 bug,高并发下泄漏
- 避免在循环里无节制
go,建议配合chan控制并发度,例如最多同时跑 30 个:
$chan = new SwooleCoroutineChannel(30);for ($i = 0; $i < 100; $i++) { $chan->push(true); // 占位 go(function () use ($i, $chan) { defer { $chan->pop(); }; // 完成后释放 $cli = new SwooleCoroutineHttpClient('httpbin.org', 443, true); $cli->get("/delay/" . rand(1, 3)); });}
协程 HTTP 客户端能复用连接吗?
可以,但仅限于同一个 $cli 实例的连续请求(keep-alive),且服务端必须支持。默认开启 keep_alive,但注意:
- 复用只发生在同一协程内多次调用
get/post;跨协程的$cli实例互不影响 - 若中间有重定向(
$cli->set(['follow_location' => true])),keep-alive 可能失效,连接被重置 - 调用
$cli->close()后连接释放,下次需重建;不调用则由协程结束时自动回收 - 连接池更稳妥:自己封装一个
HttpClientPool,用Channel管理空闲$cli,避免反复创建开销
真正难处理的不是并发本身,而是错误传播、超时分级、连接池回收时机和 fd 资源竞争——这些地方一不留神就会在线上突然卡住或内存缓慢上涨。