CDN加OBS发布时我踩到缓存键坑

举报
yd_272744321 发表于 2026/11/05 10:46:53 2026/11/05
【摘要】 这次记录的是我把官网静态资源放在 OBS、前面挂华为云 CDN 后出现的缓存错乱。发布后 43 分钟内出现白屏,我误判成 Service Worker 问题,后来用 curl 和响应头定位到 CDN 忽略 URL 参数。文中给出实际配置、脚本、命中率和账单变化。

故障出现和我走错的路

7月18日20:37,我把官网前端 v3.14.6 发到华东-上海一的 OBS 桶 portal-static-prod,这次对应的 Git 提交是 9f2c7ab。发布链路没改,CodeArts 构建后把产物推到一台 ECS,再由 obsutil 上传到 OBS。机器是 c7.large.2,2 vCPU、4GB 内存、40GB ESSD,Ubuntu 22.04.4 LTS,出网带宽 5M。流水线分段日志里,pnpm install 41s,pnpm build 2m18s,上传 1m47s,CDN 刷新接口 20:40:56 返回 200,整条链路 6 分 11 秒。

我那天晚上手动刷了312个URL。

20:41 值班群贴了三张白屏截图,我自己刷新十次只复现三次,用户侧却持续报错。Sentry 同时冒两个错误,轮着出现,一个是 chunk 加载失败,一个是运行时拿不到模块。这个组合很烦,因为看起来像两件事,其实是同一条缓存链路导致的混版本。

ChunkLoadError: Loading chunk 812 failed. (error: https://static.team.example.com/assets/app.js?v=1721306282)

GET https://static.team.example.com/assets/chunk-812.js?v=1721306282 net::ERR_ABORTED 404

TypeError: Cannot read properties of undefined (reading 'call') at app.js:2:174392

  • CDN 域名:static.team.example.com
  • 源站:portal-static-prod.obs.cn-east-3.myhuaweicloud.com
  • 前端:Vite 5.4.2,Node.js 20.11.1,pnpm 9.7.0
  • 发布频率:工作日每天 2 到 4 次
  • 旧缓存规则:*.js 和 *.css TTL 604800 秒,HTML TTL 300 秒,URL 参数设置为忽略全部
  • 历史构建配置:入口文件名锁死成 app.js、vendor.js,版本只靠 query 参数

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

20:46 到 21:09 我先盯 Service Worker,怕老 SW 拿着旧清单不放。无痕窗口、禁用缓存、清站点数据都试了,还是复现。控制台跑 navigator.serviceWorker.getRegistrations().then(r => console.log(r.length)),输出是 0,这条路白走了 23 分钟。接着我怀疑 OBS 上传不完整,做对象清点,构建目录 684 个对象,桶里也是 684 个,文件总大小差 0 字节。为了确认不是本地代理污染,我还在 ECS 上直接 curl 同一个 URL 两次,结果一个 200 一个 404,时间间隔不到 30 秒。

obsutil ls obs://portal-static-prod/ -s | grep -E 'app.js|vendor.js'
obsutil ls ./dist/assets -s | wc -l
curl -I 'https://static.team.example.com/assets/app.js?v=1721306282'
curl -I 'https://static.team.example.com/assets/chunk-812.js?v=1721306282'

21:12 我做了第一个无效改动,把 CDN 的 *.html TTL 从 300 秒改成 30 秒,规则优先级填了 30,平台提示预计 5 到 10 分钟生效,实际 6 分钟生效。监控里白屏率从 28.4% 到 27.9%,几乎没动。21:20 我又发起全站预热,684 个对象跑了 23 分钟,命中率从 95.8% 掉到 91.7%,工单没少。更尴尬的是那 312 条手动刷新 URL,任务详情写着提交 312、受理 2、重复 310,我那时还没反应过来平台按“忽略参数”做缓存键,不同 v 值在它眼里就是同一个路径。

改动细节、费用和还没做完的事

我把两个版本号请求头并排看,才回到正路。两次请求的 Age 都是 516233,节点都在 hcdn-sha-03,这已经很直白了,边缘缓存键没吃 query 参数。我又把响应体拉下来算哈希,两个文件完全一致,说明 v=1721306282 和 v=1721307000 实际命中的是同一个缓存对象。

HTTP/1.1 200 OK
Content-Length: 913245
Cache-Control: public,max-age=604800
Age: 516233
X-Cache: HIT from hcdn-sha-03
ETag: 0037B9A6F4A7F1C2

curl -s 'https://static.team.example.com/assets/app.js?v=1721306282' -o /tmp/app_old.js
curl -s 'https://static.team.example.com/assets/app.js?v=1721307000' -o /tmp/app_new.js
sha256sum /tmp/app_old.js /tmp/app_new.js
4f19f5e2f7d3b9f1d9e7a4df8e7ce6f3b2f2c1c5f2f60a8c0af0f6dd4b0db792  /tmp/app_old.js
4f19f5e2f7d3b9f1d9e7a4df8e7ce6f3b2f2c1c5f2f60a8c0af0f6dd4b0db792  /tmp/app_new.js

当晚止血我只改一处,CDN 的 URL 参数策略从“忽略全部”改成“保留全部参数”,保存时间 21:24,业务侧 21:32 观测到生效。白屏告警在 9 分钟内从每分钟 17 次降到每分钟 1 到 2 次。我中间还有一次失败尝试,21:27 给 /assets/app.js 单独加 TTL 0 规则,想着只让入口脚本回源。结果规则优先级配成了 60,*.js 规则是 20,平台按数字小的先命中,等于白改;我用 curl -I 看到还是 Cache-Control: public,max-age=604800,当场确认没生效。

第二天我把发布方式彻底改掉,不再赌刷新缓存。旧 Vite 配置是固定文件名,这个坑就是我自己之前图省事埋的。我先只改了 entryFileNames,没改 chunkFileNames,预发跑了 7 分钟又出现一次 chunk 404,立刻回退。第二版才一次改全:入口、chunk、静态资源都改 hash 文件名,HTML 走 no-cache,发布只刷新两个 HTML。下面是我最终落地的配置和命令。

// vite.config.ts 旧
entryFileNames: 'assets/app.js'
chunkFileNames: 'assets/[name].js'
assetFileNames: 'assets/[name].[ext]'

// vite.config.ts 新
entryFileNames: 'assets/[name]-[hash].js'
chunkFileNames: 'assets/[name]-[hash].js'
assetFileNames: 'assets/[name]-[hash][extname]'
pnpm build
obsutil cp dist/assets/ obs://portal-static-prod/assets/ -r -f -meta='Cache-Control:public,max-age=31536000,immutable'
obsutil cp dist/index.html obs://portal-static-prod/index.html -f -meta='Cache-Control:no-cache,max-age=0,must-revalidate'
obsutil cp dist/admin.html obs://portal-static-prod/admin.html -f -meta='Cache-Control:no-cache,max-age=0,must-revalidate'

这次返工我自己花了 1 小时 12 分钟,联调和回归 18 分钟,最花时间的是核对懒加载路由和 sourcemap 路径。7月18日 21:00 到 24:00,CDN 命中率从 95.8% 掉到 82.6%,OBS 回源多了 11.4GB,账单多 6.3 元。7月19日到7月25日换成 hash 文件名后,平均命中率回到 96.2%,同等访问量下回源从 74.8GB 降到 21.7GB,URL 刷新量从每天 200 到 400 条降到 2 条。

预发环境还在用固定文件名,我还没把多环境脚本合并,线上和预发缓存行为依旧不一致,这周我准备把同一套 Rollup 输出和 CDN 参数策略同步过去,再补一条“发布后抽样比对 Age 和 ETag”的自动检查。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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