如何根据文件名哈希自动刷新 CDN
解读
国内面试场景下,面试官真正想考察的是“构建产物带哈希 → CDN 缓存失效 → 用户端无延迟拿到最新资源”这一整条链路的闭环能力。
Grunt 作为任务编排器,核心职责是在构建阶段为静态资源注入哈希并生成一份“资源映射表”,随后把这份映射表交给发布/运维环节去驱动 CDN 刷新。
因此回答必须同时覆盖:
- Grunt 侧如何稳定、可缓存地生成带哈希的文件;
- 如何低耦合地把“新文件名清单”输出给下游(CI、运维、CDN 厂商);
- 如何低成本、高可靠地触发 CDN 刷新(含国内主流云厂商的接口差异)。
知识点
- grunt-hash、grunt-filerev 等插件的哈希算法选项(md5、sha256)与长度截取策略
- manifest 文件格式规范(JSON/KV),供下游脚本读取
- CDN 刷新方式:
– URL 刷新(精确到带哈希的文件,国内云厂商均免费额度较高)
– 目录刷新(慎用,全网生效 5-10min,易误伤) - 国内云厂商 OpenAPI 差异:
– 阿里云:支持批量 URL 刷新,一次 1000 条,QPS 限制 100;需STS 临时密钥或RAM 子账号最小权限
– 腾讯云:支持刷新/预热两个接口,预热会主动推送到边缘节点,适合大版本上线
– 七牛、又拍:需提供文件完整 URL 列表,返回任务 ID 供轮询 - Grunt 任务链顺序:clean → filerev → hashres → manifest → upload → cdn_flush,必须串行防止竞态
- 缓存命中率优化:只对内容变化的文件重新哈希,未变化文件复用旧哈希,降低 CDN 刷新量
答案
- 安装并配置 grunt-filerev 与 grunt-filerev-assets
npm i -D grunt-filerev grunt-filerev-assets
Gruntfile 片段:
filerev: {
options: {
algorithm: 'md5',
length: 8
},
assets: {
src: ['dist/**/*.{js,css,png,jpg}']
}
},
filerev_assets: {
options: {
dest: 'dist/assets-map.json', // 生成资源映射表
pretty: true
}
}
- 注册串行任务,保证先哈希再上传
grunt.registerTask('build', ['clean:dist', 'copy', 'filerev', 'filerev_assets', 'upload']);
- 在 CI 阶段读取
assets-map.json,提取所有带哈希的新 URL
示例 Node 脚本(cdn_flush.js):
const map = require('./dist/assets-map.json');
const urls = Object.values(map).map(file => `https://cdn.xxx.com/${file}`);
const ali = require('@alicloud/pop-core');
const client = new ali({
accessKeyId: process.env.ALC_KEY,
accessKeySecret: process.env.ALC_SECRET,
endpoint: 'https://cdn.aliyuncs.com'
});
await client.request('RefreshObjectCaches', {
ObjectPath: urls.join('\n'),
ObjectType: 'File'
});
- 将脚本加入 CI 最后一步,仅当构建成功且上传完成后触发;失败时通过钉钉/飞书机器人报警,人工二次确认。
- 为了灰度可控,可在
assets-map.json中增加version字段,与 Git commit id 绑定,CI 中先刷新预发布 CDN 域名,验证 200 状态码后再刷新正式域名。
拓展思考
- 双哈希策略:对第三方库使用 contenthash,对业务代码使用 chunkhash,进一步细化缓存粒度
- 边缘函数刷新:国内部分云厂商支持 EdgeRoutine,可在节点侧对比
ETag实现毫秒级刷新,无需调用 OpenAPI - 回源 302 方案:构建时不上传带哈希文件,而是上传版本清单到源站,CDN 回源时 302 到最新哈希文件,零刷新即可生效,但对源站 QPS 要求较高
- 合规审计:金融、政企项目要求每一次刷新留痕,可将
assets-map.json与刷新返回的RequestId一并写入审计数据库,供后续溯源