必须在http网关层限速,因composer自身无带宽控制能力;需通过nginx/envoy按租户令牌桶限流或artifactory仓库配额实现,同时确保缓存与镜像路径租户隔离。

跨机房高频拉取根本不能靠 Composer 自身控制带宽——它压根没这个能力,必须在 HTTP 网关层拦截和限速。
为什么 composer install --prefer-dist 无法限速
Composer 客户端所有下载行为都走 cURL,http-max-concurrent-downloads 控制的是并发连接数,不是带宽;--prefer-dist 只影响包来源(zip/tarball vs git clone),不改变单个请求的传输速率。你设再高的超时、再低的并发,只要镜像服务没做限流,出口带宽就可能被某租户一次 composer install 打满。
- 错误认知:“调小
parallel-downloads就能省带宽” → 实际只是降低请求数,每个 .zip 仍会跑满 TCP 连接带宽 - 真实瓶颈:跨机房链路通常有 QoS 策略或物理带宽上限(如 100MB/s),单次拉取几十个 dist 包,瞬间打爆
- 日志里看到
Downloading https://... (25 concurrent)并不表示带宽可控,只说明开了 25 个连接
Nginx 或 Envoy 前置网关按租户限速
在镜像服务前加一层反向代理,用请求头(如 X-Tenant-ID)识别租户,再套令牌桶限速。这是目前最轻量、最可控的方式。
- 示例 Nginx 配置片段:
limit_req_zone $http_x_tenant_id zone=tenant_rate:10m rate=10r/s; location / { limit_req zone=tenant_rate burst=30 nodelay; proxy_pass http://composer-mirror-backend; } - 关键点:
rate=10r/s是请求频次限制,不是字节限速;若需真正控带宽(如 5MB/s),得配合limit_rate+ 动态变量,但需注意它作用于整个响应体,对大 zip 文件更有效 - 必须确保上游镜像服务返回的响应头含
Content-Length,否则limit_rate在流式响应下效果不可控
Artifactory/JFrog 私有仓库的 per-repository 配额
如果你用 Artifactory 托管 Composer 镜像,直接在 UI 或 REST API 中为每个租户绑定独立仓库,并开启带宽配额,比自己搭网关更稳。
- 操作路径:
Admin → Repositories → tenant-a-composer → Configuration → Bandwidth Quota - 可设硬上限(如
10MB/s),超限后返回HTTP 429 Too Many Requests,Composer 会自动重试(默认 3 次) - 注意:配额是按 repository 维度,不是按用户 token;共用一个 virtual repo 的租户无法隔离,必须拆成多个 physical repo
- API 示例:
PUT /api/v2/repositories/tenant-b-composer/bandwidthQuota {"enabled":true,"limit":10485760}(单位 byte/s)
别碰 Docker 构建时的 COMPOSER_CACHE_DIR 共享
多租户 CI 流水线如果共用 COMPOSER_CACHE_DIR(比如都指向 /tmp/composer-cache),不仅会污染缓存,还会让限速策略失效——因为不同租户的请求混在同一个 IP 出口,网关无法区分。
- 正确做法:每个租户构建前生成唯一缓存目录,例如
COMPOSER_CACHE_DIR=/tmp/composer-cache-$(uuidgen) - 同时在镜像 URL 后拼租户标识,如
https://mirror.example.com/tenant-a/,确保网关能从 path 提取租户上下文 - 危险操作:
RUN composer config -g cache-dir /tmp/cache—— 这会让所有后续 RUN 指令共享同一缓存,且破坏隔离边界
真正卡住跨机房带宽的,从来不是 Composer 版本或参数,而是你有没有把限速逻辑落在 HTTP 层、有没有让每个租户的流量可识别可计量。漏掉租户路由或缓存隔离,再精细的网关规则也白搭。











