CDN回源OBS时的缓存返工记录

举报
yd_292017662 发表于 2026/10/28 10:29:55 2026/10/28
【摘要】 团队把 H5 静态资源放在华为云 OBS,再挂 CDN 后,发布当天出现版本错配,用户拿到旧 index 和旧 config。这里是我 4 小时排查的记录,含请求头、命令、刷新限额报错、缓存规则调整,以及改造前后命中率和费用数字。

线上报错出现的那半天

10 月 18 日 09:43,Sentry 项目 team-a-web 的错误曲线在 5 分钟内从 8 条跳到 327 条,告警 ID 8f0d1c2a。我点开采样事件看到 release=v2026.10.18.2,environment=prod,主报错还是 Uncaught TypeError: Cannot read properties of undefined (reading 'tenantId')。堆栈指到 app.9e3f2.js:1:44219,source map 反解在 initTenant() 第 67 行,调用链是 bootstrapApp -> loadRuntime -> initTenant。发版流水线是 CodeArts #17432,Node.js 20.11.1,pnpm 9.12.0,构建 6 分 41 秒,286 个文件,总大小 73.4 MB,单测 412/412 通过,安全扫描没有阻断。

这个锅我一开始甩给前端了。

11:06 客服把 19 张截图丢到群里,控制台除了 TypeError 还有 GET https://static.team-a.example.com/assets/app.9e3f2.js net::ERR_ABORTED 404,其中 7 张图是旧 hash 文件 app.6c1ad.js。我们抓了 3 份用户 HAR,同一租户在 11:03 到 11:09 间拿到两版 index.html,ETag 分别是 '8f3a7-18f8-6510a0cf' 和 '8e120-18e2-650ff1b1'。UA 里有 Chrome 129、Edge 128、iOS Safari 17,这个分布和纯代码缺陷对不上,更像缓存层入口页不一致。

当时的部署和规格

静态资源在华北-北京四 OBS 桶 prod-h5-static,桶私有读,CDN 回源走鉴权参数,签名有效期 300 秒。业务日活 2.1 万,11:30 到 13:00 峰值带宽常在 180 Mbps。老发布脚本图省事,dist 整包上传并统一写 Cache-Control:max-age=86400,HTML、JS、runtime.json 一个策略,平时问题不明显,发版窗口就会把错配放大。

  • OBS 上传工具:obsutil 5.5.12
  • 探测机 1:ECS s7n.large.2(2 vCPU / 4 GiB),广州,Ubuntu 22.04
  • 探测机 2:ECS s7n.large.2(2 vCPU / 4 GiB),上海,Ubuntu 22.04
  • 两台探测机开 6 小时,费用 3.96 元,单台 0.33 元/小时

我用 obsutil stat 查了对象元数据,index.html 的 Last-Modified 是 10:12:07,runtime.json 是 10:29:31,差 17 分 24 秒,两者 Cache-Control 都是 max-age=86400。流水线 upload-dist 阶段里还出现过 PutObject retry 1/3, err=EOF,说明上传本身就有重试,先后顺序更不稳定。

我怎么确认是缓存错配

11:21 我在两台 ECS 同时跑抓头脚本,每分钟请求 index.html 和 runtime.json,连续 30 分钟,还加了两个查询参数版本。脚本在 /opt/probe/check_cache.sh,用的是 curl -sSI --http2 --connect-timeout 2,日志落盘 /tmp/cdn_probe.log。后面用 awk 统计 age,广州节点 p95=12103,上海 p95=11988,基本只增不减。

curl -I https://static.team-a.example.com/config/runtime.json?v=20261018
HTTP/2 200
cache-control: max-age=86400
age: 11791
x-cache: HIT from cdn-sh-02
etag: '5f29-7a1-6510a13d'

curl -I https://static.team-a.example.com/config/runtime.json?v=20261018-2
HTTP/2 200
cache-control: max-age=86400
age: 11853
x-cache: HIT from cdn-sh-02
etag: '5f29-7a1-6510a13d'

curl -I -H 'Cache-Control: no-cache' https://static.team-a.example.com/config/runtime.json?v=20261018-3
HTTP/2 200
age: 11907
x-cache: HIT from cdn-sh-02

三个参数拿到同一 ETag,手工 no-cache 也没触发回源。CDN 控制台里那条规则写的是忽略 URL 参数=是,前端靠 ?v 时间戳换配置这个手段直接失效,index.html 又被缓存一天,老页面继续引用旧 hash,404 和 TypeError 就一起出现了。

我在 00:37 才停掉全量刷新。

返工了两次

12:04 我做第一次返工,直接调 CDN 刷新接口清全站,脚本每批 500 条 URL,连跑 7 轮。接口是 POST /v1.0/cdn/domains/{domain_id}/refresh-files,并发参数开到 6。第 4 轮开始限频,返回 429,request-id 9f42b6d1。12:16 监控掉到 41 条/5 分钟,12:31 又反弹到 190 条/5 分钟,节点回源后继续拿到 max-age=86400,等于只把旧缓存换成了新的长期缓存。

refresh failed: status=429
body={'message':'Too Many Requests','request_id':'9f42b6d1'}
retry-after: 60

我还做过一次失败尝试,走前端热修,把 runtime 请求改成 fetch('/config/runtime.json?r='+Date.now(), {cache:'no-store'}),并在 axios 默认头补了 Pragma: no-cache。补丁发到 v2026.10.18.3,预发看着正常,线上没救回来,CDN 仍按忽略参数的缓存键命中同一对象,r 再变也没用。13:22 我回滚到 v2026.10.18.2,流水线 #17435,回滚耗时 9 分 14 秒。

第 2 次返工我白干了 27 分钟。

那次我只改了 CDN 配置,把 *.html TTL 设成 60 秒,但规则还勾着遵循源站,回来的响应头仍是 max-age=86400;而且这条规则优先级排在 /assets/* 后面,请求有一部分落到默认规则,age 继续累加,我盯日志时才意识到改动没有真正生效。

改动内容

17:20 我把发布脚本拆成两段,并明确上传顺序是 assets -> runtime.json -> index.html。原因很现实,先放齐带 hash 的静态文件,再放配置,收尾才发布入口页,避免入口先可见但依赖文件还没到位。流水线多了 43 秒,从 6 分 41 秒变成 7 分 24 秒,我愿意付这点时间成本。

# 旧脚本
obsutil cp dist/ obs://prod-h5-static/ -r -f -meta=Cache-Control:max-age=86400

# 新脚本
obsutil cp dist/assets/ obs://prod-h5-static/assets/ -r -f -meta=Cache-Control:max-age=2592000,immutable
obsutil cp dist/config/runtime.json obs://prod-h5-static/config/runtime.json -f -meta=Cache-Control:max-age=30,must-revalidate
obsutil cp dist/index.html obs://prod-h5-static/index.html -f -meta=Cache-Control:no-cache
obsutil setmeta obs://prod-h5-static/config/runtime.json -meta=Content-Type:application/json;charset=utf-8

CDN 规则也重排了,优先级 10 匹配 *.html,TTL=0 且遵循源站;优先级 20 匹配 /config/*,TTL=30 秒并强制校验 ETag;优先级 30 匹配 /assets/*,TTL=30 天,URL 参数参与缓存键。17:48 到 19:32 我做 10% 灰度,只放华南和华东。19:41 我手滑把 runtime.json 设成 no-store,OBS GET QPS 从 38 冲到 226,5 分钟后撤回,20:03 才稳定。10 月 21 日全天数据,CDN 命中率从 63.2% 到 93.7%,OBS GET 从 1,942,311 次/天降到 318,044 次/天,请求费用从 21.37 元/天降到 3.86 元/天,回源流量从 52.6 GB/天降到 8.9 GB/天。同类报错从 327 条/5 分钟降到 6 条/5 分钟,剩下 6 条里 4 条是用户插件改写脚本,2 条来自公司内网旧代理缓存。

我还没解决移动端弱网下首屏 CSS 1.8 秒空白,下一步准备把关键 CSS 内联到 6 KB 以内,再把 CDN Brotli 只对 text/css 和 application/javascript 开 5% 灰度,连续观察 48 小时的 LCP p75、回源带宽和 4xx 比例。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。