Composer默认不支持并发下载,因其HttpDownloader基于PHP stream_context_create,无法并行请求,且为保障兼容性、稳定性及服务端压力控制而刻意设计为串行。

Composer 默认根本不支持并发 HTTP 请求
别被“多线程下载”这类说法带偏——composer install 默认是串行拉取每个包的 composer.json 和 ZIP 文件,底层用的是 PHP 的 stream_context_create,它不支持真正的并行连接。这不是 bug,是刻意设计:避免触发防火墙限流、代理拦截,或压垮 Packagist 服务端。
常见误判现象:
- 执行
composer install卡在 “Downloading xxx” 长达几十秒,以为是网络慢,其实是串行排队 - 看到
hirak/prestissimo或composer-plugin-parallel-downloader就直接装,结果 PHP 没启用ext-curl,插件静默降级回串行 - 企业内网镜像源不支持
Range请求头,插件强行并发导致大量416 Requested Range Not Satisfiable错误
验证是否真在并发:
- 运行
strace -e trace=connect,sendto,recvfrom composer install 2>&1 | grep -c 'connect'(Linux),串行时连接数 ≈ 包数量 × 2;并发插件启用后应明显少于该值 - 观察进程网络连接:
ss -tnp | grep :443 | grep composer,并发时能看到多个 ESTABLISHED 连接
如何确认并启用有效的并发下载插件
官方不提供 --threads 这类参数,但可通过替换 HttpDownloader 实现并发。目前唯一稳定可用的是 composer-plugin-parallel-downloader(hirak/prestissimo 已归档且不兼容 PHP 8.2+)。
实操步骤:
- 先确认环境:运行
php -m | grep curl确保curl扩展已加载;php -i | grep allow_url_fopen确保为On - 全局安装插件:
composer global require "kylekatarnls/composer-installers-extender"不需要;正确命令是:composer global require "symfony/flex"是干扰项 → 直接运行:composer global require "squizlabs/composer-installer"也不对 → 最终只需:composer global require "composer-plugin-parallel-downloader" - 验证是否生效:执行
composer install -v,输出中若出现类似[ParallelDownloader] Downloading X packages in parallel即表示启用成功 - 注意兼容性:该插件依赖 cURL multi,某些 Alpine 容器镜像需额外装
curl-dev和gcc编译支持
比并发更关键的提速动作:强制走 dist + 切换国内镜像
并发下载收益常被高估,真正起决定性作用的是「要不要 clone Git」和「连哪个源」。一个 Laravel 包用 --prefer-source 可能要花 8 秒克隆,而 --prefer-dist 下 ZIP 通常只要 0.5 秒。
必须做的配置:
- 全局设 dist 优先:
composer config -g prefer-stable true+composer config -g prefer-lowest false(非必需),核心是:composer config -g use-include-path false+ 显式加--prefer-dist参数 - 国内镜像必须配,否则直连
packagist.org在 DNS 解析和 TLS 握手阶段就卡住:composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ - 验证镜像生效:
composer config -g repo.packagist输出应为https://mirrors.aliyun.com/composer/,不是packagist.org或空值 - CI/CD 中所有
composer install命令都必须显式带上--prefer-dist,不能依赖全局配置,防止 runner 环境污染
并发脚本 ≠ Composer 并发执行 scripts
很多人想让 "scripts": {"a": "cmd1", "b": "cmd2"} 并发跑,但 composer run-script a b 仍是串行:等 cmd1 完全退出才启动 cmd2。Composer 本身没有任务调度能力,scripts 字段只是 shell 命令列表。
真实可行的并发路径只有这一条:把并发逻辑写进 PHP 脚本里,用 guzzlehttp/promises 管理 I/O。
- 安装:
composer require guzzlehttp/promises - 脚本内用
Promise\all()同时发起多个$client->getAsync(),不是靠开多个proc_open进程 - 关键陷阱:
Promise\wait()会阻塞当前进程,CLI 场景下没问题;但若在 FPM 中调用,必须确保max_execution_time足够,且不能依赖sleep()控制节奏 - 错误传播容易漏:
Promise\settle()返回混合状态数组,需手动检查每个reason或value,不能只看isFulfilled()
最常被忽略的一点:并发请求的吞吐提升,完全依赖后端 API 的响应时间分布。如果所有接口平均耗时 2s 且强相关(比如共用同一数据库),并发带来的总耗时下降可能不到 10%,此时优化 DB 查询或加缓存,远比折腾并发更有效。











