描述在 grunt 中实现灰度缓存策略的思路
解读
国内前端面试常把“灰度缓存”与“构建工具”结合考察,目的是看候选人能否把业务发布节奏(灰度)与静态资源缓存(CDN 缓存、本地缓存)打通。Grunt 作为老牌任务运行器,本身只做构建,但可以通过插件编排 + 自定义任务把“灰度”与“缓存”两个维度串起来。面试官想听到的是:
- 如何利用 Grunt 的插件体系产出带指纹的资源;
- 如何按灰度维度(版本号、用户分组、地域、机房)生成多份资源;
- 如何在 HTML/SSR 模板里动态注入对应灰度的资源地址;
- 如何保证回滚与灰度切换时缓存立即失效;
- 整个流程可灰度、可回滚、可审计,且对运维、CDN、Node 层零侵入。
知识点
- grunt-filerev 或 grunt-hash——给静态资源加 MD5 指纹,解决“强缓存”问题
- grunt-usemin——自动把 HTML 里引用的静态资源替换成带指纹的路径
- grunt-file-creator / grunt-template——按灰度维度动态生成多份入口 HTML
- grunt-contrib-copy + grunt-contrib-uglify/cssmin——多目录输出,实现“物理灰度”
- process.env.BUILD_GRAY——通过环境变量把“灰度标识”注入 Grunt 运行时,实现一次构建多条流水线
- grunt-contrib-clean——每次构建前清理旧灰度目录,防止“指纹残留”导致 CDN 回源错乱
- grunt-git-rev——把 git commit id 写入灰度目录名,实现版本可溯源
- grunt-ssh-deploy / grunt-scp——把灰度资源并发上传到 CDN 源站的不同路径,例如
/gray/1.2.3/_next/ - grunt-contrib-watch + grunt-contrib-livereload——本地联调时按灰度分组快速刷新
- 缓存策略头:CDN 侧对
/gray/{version}/**设置Cache-Control: max-age=31536000, immutable,对/gray/current/**设置Cache-Control: max-age=60, s-maxage=60——实现灰度路径长期缓存、入口路径短时缓存 - Node 灰度网关——根据用户 cookie/uid 段决定渲染
current还是{version}目录下的 HTML,Grunt 只负责把两份资源提前编译好
答案
整体思路分三层:构建层、部署层、运行时层,Grunt 重点搞定前两层。
-
构建层
a. 在 Gruntfile 里先用grunt.option('gray')读取命令行参数,例如grunt build --gray=1.2.3,缺省则为stable。
b. 通过grunt.config.merge动态把dest目录改成dist/gray/<gray>/,实现物理隔离。
c. 串行任务:
clean → copy → uglify/cssmin → filerev → usemin → template
其中filerev会把app.a8b3.js改成app.a8b3.987fed.min.js,usemin自动替换 HTML 里的引用。
d. 用grunt-file-creator生成一份gray.json,内容{ "version": "1.2.3", "gitRev": "e4f3a2", "resourceMap": {...} },供 Node 网关动态拼接资源域名。
e. 如果要做用户分组灰度,再跑一遍grunt build --gray=1.2.3 --group=10%,输出到dist/gray/1.2.3_10/;CI 里用矩阵并行,10 分钟以内可出 5 组灰度包。 -
部署层
a. 通过grunt-scp把dist/gray/1.2.3/上传到 CDN 源站/gray/1.2.3/目录,并做rsync --delete保证原子覆盖。
b. 上传完成后调用 CDN 刷新接口,只刷/gray/1.2.3/**,避免整站缓存失效。
c. 在灰度控制台把1.2.3标记为“待灰”,此时线上流量仍为 stable 目录。 -
运行时层(Grunt 不直接参与,但要预留规范)
Node 网关读取gray.json,按用户 uid 尾号决定渲染1.2.3还是stable目录下的 HTML;回滚时把灰度标记改为stable即可,无需重新构建。
通过以上三步,Grunt 侧实现了“一次构建、多灰度包、指纹缓存、秒级回滚”的灰度缓存策略,且全程可脚本化、可回滚、可审计。
拓展思考
- 增量指纹:大项目全量哈希慢,可结合
grunt-newer只给变更文件重新生成指纹,缩短 CI 时间 60%。 - 边缘灰度:如果 CDN 支持 EdgeRoutine,可在边缘脚本里读取灰度配置,把
app.987fed.js302 到app.987fed_gray.js,实现无发布灰度,Grunt 只需多输出一份_gray后缀文件即可。 - 共享缓存:公共库(react、lodash)单独做
vendor.dll.js,指纹不变时各灰度版本共用,减少 CDN 流量 30%。 - 合规审计:在 Gruntfile 里加
grunt.event.on('exit', () => { axios.post('/log', { gray, user: process.env.BUILD_USER }) }),把灰度构建记录实时推送到内部审计系统,满足国内金融、政务项目上线必留痕的合规要求。