CDN和用户本地的缓存如何更新

11 阅读4分钟

一、缓存策略与生命周期

1. CDN缓存机制

  • 强缓存:通过Cache-Control: max-age=3600Expires控制,CDN直接返回本地副本(无需请求服务器);
  • 协商缓存:通过ETagLast-Modified验证资源是否更新,若未更新则返回304状态码;
  • CDN节点层级:边缘节点(离用户近)→ 区域节点→ 源站,逐层回源验证。

2. 用户本地缓存

  • 浏览器缓存:HTML、CSS、JS、图片等静态资源按HTTP头规则缓存;
  • Service Worker:可拦截网络请求,实现离线缓存(需手动控制更新)。

二、缓存更新触发机制

1. CDN侧更新

  • 主动刷新(Purge)
    • 通过CDN服务商API手动刷新指定资源(如curl -X PURGE https://cdn.example.com/app.js);
    • 适用于紧急更新(如修复JS漏洞),刷新后CDN节点会重新从源站拉取资源。
  • 缓存时间到期
    • 资源超过max-age设置的时间后,CDN自动回源验证。

2. 用户本地缓存更新

  • 资源URL变更
    • 通过文件名哈希(如app.1234abcd.js)强制用户下载新资源;
    • 适用于HTML中引用的静态资源(如CSS、JS)。
  • Service Worker更新
    • 注册新的Service Worker脚本,通过self.skipWaiting()强制激活;
    • 使用Cache Storage API删除旧缓存(如caches.delete('old-cache'))。

三、版本控制最佳实践

1. 文件名哈希(推荐方案)

  • 实现方式
    • 构建工具(如Webpack、Vite)自动生成带哈希的文件名(如app.[contenthash].js);
    • HTML中动态引用新文件名(如通过模板替换)。
  • 优势
    • 强缓存命中率高(不同版本URL不同);
    • 无需手动刷新CDN(新资源自动推送到CDN节点)。

2. 查询参数版本号

  • 实现方式
    • 在URL后添加版本号(如app.js?v=20230704);
    • 版本号可通过构建工具自动替换(如package.json中的version字段)。
  • 注意事项
    • CDN需配置忽略查询参数(部分CDN默认不缓存带查询参数的资源);
    • 可能导致旧资源长期留在CDN节点(需手动刷新)。

3. HTML动态加载

  • 实现方式
    // 通过JS动态加载资源,版本号存储在配置文件中
    const version = '20230704';
    document.write(`<script src="app.js?v=${version}"></script>`);
    
  • 适用场景:需要实时控制资源版本的场景(如A/B测试)。

四、风险应对与监控

1. 缓存更新失败的处理

  • 双缓存策略
    • Service Worker同时缓存当前版本和上一版本资源,确保更新失败时回退;
    • 示例:
      self.addEventListener('install', event => {
        event.waitUntil(
          caches.open('v2').then(cache => cache.addAll(['/app.js']))
        );
      });
      
  • CDN预热
    • 更新后主动向CDN节点推送新资源(如使用阿里云CDN的“预热”功能)。

2. 监控与告警

  • CDN状态监控
    • 通过服务商API获取缓存命中率、回源率等指标;
    • 设置异常告警(如回源率突然升高)。
  • 用户端监控
    • 通过JS埋点统计用户加载失败率;
    • 使用Performance API监测资源加载时间。

五、问题

1. 如何确保HTML文件本身被更新?


  • HTML通常设置较短的缓存时间(如Cache-Control: max-age=60)或禁用强缓存(Cache-Control: no-cache),确保每次请求都验证最新版本。
    同时,HTML中引用的静态资源(如CSS、JS)使用哈希文件名,保证内容变更时强制更新。

2. CDN缓存刷新后,用户仍看到旧资源怎么办?

    1. 检查源站:确认源站文件是否已更新;
    2. CDN节点预热:主动向边缘节点推送新资源;
    3. 用户端强制刷新:引导用户使用Ctrl+F5(Chrome)或清除浏览器缓存;
    4. 紧急降级:通过Service Worker回退到上一版本资源。

3. 如何平衡缓存命中率与更新及时性?

    • 静态资源(如JS、CSS、图片):使用长缓存(如max-age=31536000)+ 哈希文件名,确保内容不变时充分利用缓存;
    • 动态资源(如API响应):使用短缓存或协商缓存(如ETag),保证数据实时性;
    • HTML文件:设置中等缓存时间(如5分钟),平衡加载速度与更新频率。

六、总结

  • 静态资源:哈希文件名 + 超长缓存(1年),无需手动刷新CDN;
  • HTML/动态内容:短缓存 + 协商验证,确保及时更新;
  • 紧急情况:CDN手动刷新 + Service Worker双缓存回退;
  • 监控保障:CDN指标监控 + 用户端异常告警。