解释在 CDN 缓存下实现平滑版本回滚的方案

解读

面试官想确认两点:

  1. 你是否理解 CDN 边缘节点缓存带来的“版本滞后”风险;
  2. 你是否能用 Grunt 这类构建工具把“回滚”做成一条可灰度、可观测、可一键回退的流水线,而不是“删缓存、等 10 分钟、全站 502”的野路子。
    国内主流 CDN(阿里云 CDN、腾讯云 ECDN、百度云加速)默认缓存时间 10 min~24 h,且节点分布广,TTL 未到期时用户仍可能拿到旧资源;同时微信、QQ、UC 等超级 App 自带私有缓存,进一步放大回滚难度。 因此方案必须“双轨”:一边让 CDN 快速失效,一边让页面能瞬间指向另一套干净资源。

知识点

  1. Grunt 指纹化(grunt-rev / grunt-filerev):在文件名中注入 8 位 hash,实现“文件不变则 URL 不变,文件一变则新 URL”,让旧版本天然成为不可变对象。
  2. Grunt 路径替换(grunt-usemin / grunt-replace):自动把 html/css/js 中的静态引用替换成带 hash 的新 URI,保证上线即命中 200 新资源。
  3. 非覆盖式发布:每次构建输出到独立目录 /release/v{version}.{timestamp}/,回滚只需把 html 入口的 window.resourceBase 指向上一个目录即可,无需清理 CDN。
  4. 国内 CDN 缓存失效手段
    • 目录刷新(阿里云 RefreshDir):支持通配 https://static.example.com/release/v1.2.3/*,平均 5~7 min 全国失效;
    • 预热(PushCache):回滚后立刻把旧版本重新预热到边缘,降低回源带宽。
  5. 灰度回滚:通过 Cookie/Header 把 5% 用户流量打到新版本,出问题瞬间把边缘规则切回旧版目录,实现“秒级”回滚。
  6. Grunt 自动化钩子:在 grunt.registerTask('rollback') 里顺序执行:
    ① 调用 CDN 开放 API 刷新目录;
    ② 修改 Nginx 或 OSS 静态托管的 index.html 指向上一版本;
    ③ 触发钉钉/飞书 Webhook 通知版本号与回滚时间;
    ④ 生成回滚报告 JSON,供运维平台归档。

答案

“平滑回滚”核心思路是让版本与 URL 一一对应,再让入口文件(唯一不被 CDN 长缓存的 html)可热切换。具体步骤如下:

  1. Grunt 构建阶段
    • 使用 grunt-filerev 给所有 js/css/img 加 hash,输出到 /release/v1.2.4.202507041430/
    • grunt-usemin 把 html 里的引用同步改写;
    • 生成 manifest.json 记录版本号与文件映射,供回滚脚本读取。
  2. 发布阶段
    • /release/v1.2.4.202507041430/ 整目录上传到 OSS 并设置 Cache-Control: max-age=31536000, immutable
    • 更新 index.html(存放在根目录,缓存 30 s)中的 window.V = 'v1.2.4.202507041430',用户下次刷新即拉新资源。
  3. 回滚阶段
    • 执行 grunt rollback --to=v1.2.3.202507031200
    • 脚本调用阿里云 CDN RefreshDir 接口,刷新新版本目录,防止仍有用户被边缘节点返回 404;
    • index.html 中的 window.V 改回旧版本号,上传 OSS 并设置 Cache-Control: no-cache
    • 触发预热接口,把旧版本目录重新推送到全国边缘,保证回滚后首屏 200 ms 内加载;
    • 灰度验证 1 min 无异常,再全量开放;若仍异常,可继续回滚到更老版本,全程用户无感知。
  4. 兜底策略
    • 在页面注入 __BUILD_TIME__ 变量,前端监控发现错误率飙升时,可自动调用 location.reload(true) 强制回源,绕过本地缓存;
    • 对超级 App 内嵌 WebView,采用“带版本号的离线包”机制,回滚时直接下发旧包,实现端侧零等待。

拓展思考

  1. 双 CDN 热备:主走阿里云,备走腾讯云,Grunt 构建后并行上传两份,回滚时改 DNS 解析即可,5 min 内完成跨云切换。
  2. 边缘函数回滚:利用阿里云 CDN 的 EdgeRoutine,在回滚阶段把请求 rewrite 到旧目录,无需改源站,实现 1 min 内全球生效
  3. 自动化决策:在 Grunt 流水线里集成 Sentry 错误率 API,当 5 min 错误率 > 1% 时自动触发 grunt rollback,把“人工回滚”升级为“无人值守回滚”。