镜像站QPS日志不可信,因Composer客户端病态重试、缓存污染及NAT导致请求失真;需监控last_sync_time、本地缓存mtime和Nginx upstream错误,防御应聚焦客户端校验、空缓存挂载与项目级备用源配置。

Composer镜像站本身不对外暴露QPS指标,所谓“QPS突增突降”不是服务端真实承载压力的反映,而是下游客户端行为异常或缓存污染导致的请求模式畸变——检测和防御必须绕过传统网关限流思路,直击 Composer 的元数据缓存机制与客户端重试逻辑。
为什么镜像站的 QPS 日志不可信
镜像站(如阿里云、清华源)本质是静态文件 CDN + Nginx 反向代理,其 access_log 中的 request_time 和 upstream_response_time 无法区分真实用户请求与 Composer 客户端的“病态重试”:
- Composer 不做指数退避,
composer install遇到 502/503 会立即重试,3 次失败后直接退出,但 CI 流水线常配置retry: 3,造成单次构建触发 9+ 次相同/packages.json请求 - Docker 构建复用
~/.composer/cache卷时,一个缓存中毒的构建会引发后续所有构建反复拉取同一坏 URL,形成“请求风暴”,而 Nginx 日志里只看到一堆GET /p2/vendor/package.json HTTP/1.1 - 镜像站无 auth,无法绑定请求到具体项目或团队,
$remote_addr在 NAT 或 CDN 后全是同一个出口 IP,QPS 统计失去粒度
真正要监控的三个信号源
别看 Nginx 的 limit_req 日志,盯住这三个位置才能提前发现异常流量:
-
/data/mirror/composer/last_sync_time:如果该文件时间戳停滞超过 15 分钟,说明上游同步卡死,下游客户端开始密集轮询——此时curl -I https://mirrors.aliyun.com/composer/packages.json返回 502 的频率会陡升 -
~/.composer/cache/repo/https---mirrors-*.*/packages.json文件的 mtime:正常情况应随镜像同步每 5 分钟更新一次;若大量客户端本地该文件 2 小时未变,却持续发起请求,说明缓存被污染且未清 - Nginx error_log 中连续出现
connect() failed (111: Connection refused) while connecting to upstream:这表示 rsync 同步脚本崩溃后,本地 nginx 仍指向已关闭的 upstream(如 127.0.0.1:8080),而非返回 503,导致客户端误判为网络抖动而疯狂重试
防御动作必须在客户端侧闭环
镜像站端加限流(如 limit_req zone=composer burst=10 nodelay)只会让失败更快,并不能解决根本问题。有效防御必须由调用方控制:
- CI 脚本中禁止裸写
composer install,必须前置校验:curl -s -o /dev/null -w "%{http_code}" https://mirrors.aliyun.com/composer/packages.json | grep -q "^200$",非 200 则切源并退出,不重试 - 所有 Dockerfile 必须显式挂载空缓存:
RUN mkdir -p ~/.composer/cache && echo '{}' > ~/.composer/cache/repo/https---mirrors.aliyun.com-composer/packages.json,避免继承上层坏缓存 - 禁止使用
composer config --global repo.packagist做全局 fallback——它会被项目级repositories覆盖;正确做法是在composer.json中写死备用源:"repositories": [{"type": "composer", "url": "https://mirrors.tuna.tsinghua.edu.cn/composer/"}]
最易被忽略的“伪突增”陷阱
某次凌晨报警显示 QPS 从 2000 突增至 12000,排查发现是运维执行了 find /data/mirror/composer -name "*.json" -mtime +30 -delete,误删了 p2/ 下全部 provider 文件。结果所有新包请求都 fallback 到 /packages.json 入口,再由 Composer 客户端逐个请求缺失的 p2/vendor/name.json ——这不是攻击,是运维操作触发的元数据链路断裂。真正的防御点不在流量层,而在 inotifywait -m -e create,delete /data/mirror/composer/p2 的实时监听与告警。


















