根本原因是未限制并发数导致连接池耗尽、dns阻塞或服务端限流;正确做法是显式设置co::parallel并发上限、复用httpclient实例、分批处理并统一错误结构。

Hyperf 里用 Co::parallel 做接口批量调用,为什么没快多少?
常见现象是:明明开了协程并发,但整体耗时只比串行快一点点,甚至更慢。根本原因通常是没控制并发数,导致大量协程同时发起 HTTP 请求,触发服务端限流、连接池耗尽或 DNS 阻塞。
-
Co::parallel默认不限制并发数,100 个任务就起 100 个协程,但HttpClient的连接池默认只有 20 个空闲连接(max_connections),其余协程卡在等待连接上 - 未设置超时或重试,某个慢请求拖垮整批;DNS 解析未复用,高频请求下
getaddrinfo成瓶颈 - 错误直接抛出未捕获,一个失败导致整个
Co::parallel中断退出
正确做法是显式限制并发数,并复用客户端实例:
$client = new HttpClient(['max_connections' => 50, 'timeout' => 5.0]);
$result = Co::parallel(array_map(function ($item) use ($client) {
return function () use ($client, $item) {
try {
$resp = $client->get('https://api.example.com/item/' . $item['id']);
return ['success' => true, 'data' => $resp->getBody()->getContents()];
} catch (Throwable $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
};
}, $items), 20); // 第二个参数 20:最大并发 20 个
分批执行时,array_chunk 和 Co::parallel 怎么配合才不爆内存?
一次性把 10 万条数据 array_chunk 成 1000 批,再对每批调用 Co::parallel,看似合理,但容易忽略两点:协程栈内存累积 + GC 延迟。
- 每批
Co::parallel启动的协程不会立刻销毁,尤其当批处理多、单批协程数高时,PHP 内存持续上涨 -
array_chunk本身复制数组,大数组切分产生大量临时内存,建议用Iterator或手动索引遍历替代 - 别在循环里反复 new
HttpClient,连接池和 DNS 缓存失效,应复用同一个客户端实例
推荐写法(控制内存+复用):
$client = new HttpClient(['max_connections' => 30]);
$chunkSize = 50;
for ($i = 0; $i fn() => $client->get(...)->getBody()->getContents(), $chunk), 30);
// 主动触发 GC(可选,视数据量而定)
gc_collect_cycles();
}
HTTP 接口返回格式不一致,Co::parallel 结果怎么统一收口?
批量调用第三方接口时,常遇到部分成功、部分 4xx/5xx、部分超时、部分解析失败。如果只靠 try/catch,结果结构难统一,后续逻辑要反复判断键是否存在。
- 不要依赖
Co::parallel返回值的顺序和长度——失败协程不返回值,结果数组会变短 - 用
array_values+ 索引映射还原原始顺序,避免错位 - 所有分支路径必须返回相同结构,比如固定含
id、success、data、error字段
示例(带原始 ID 回填):
$tasks = [];
foreach ($items as $item) {
$tasks[$item['id']] = function () use ($client, $item) {
try {
$resp = $client->get('https://api.example.com/' . $item['id']);
return ['id' => $item['id'], 'success' => true, 'data' => json_decode($resp->getBody(), true)];
} catch (Throwable $e) {
return ['id' => $item['id'], 'success' => false, 'error' => $e->getMessage()];
}
};
}
$resultMap = Co::parallel($tasks, 25);
// $resultMap 是关联数组,key 是 id,无需担心顺序错乱
本地调试时 Co::parallel 没生效,是不是协程没跑起来?
最常见原因是 CLI 环境没启用协程调度器,或者用了不兼容的扩展(比如 xdebug 开启时协程会被降级为同步)。
- 检查
cli-config.php是否启用了Scheduler,Hyperf 2.2+ 默认开启,但某些 Docker 镜像或旧版本需手动配置 - 运行
php -m | grep coroutine,确认ext-swoole或ext-openswoole已加载且版本 ≥ 4.8.0 - xdebug 3.x 默认禁用协程,调试时加环境变量
XDEBUG_MODE=off或关闭 xdebug.ini - 别在
__construct或非协程上下文(如Command的handle方法未加@Coroutine注解)里直接调用Co::parallel
快速验证协程是否工作:
Co::create(function () {
echo "协程已启动\n";
Co::sleep(0.1);
echo "协程执行完成\n";
});
如果两行输出间隔明显小于 0.1 秒,说明协程调度正常。
分批并发不是开个 Co::parallel 就完事,关键在连接池水位、错误收敛、内存节奏和运行时环境。漏掉任意一环,都可能让“并发”变成“假并发”。











