我把91网页版的缓存管理拆给你看:其实一点都不玄学(信息量有点大)

前言 很多团队一开始把缓存当“黑盒”,遇到页面刷新、静态资源不更新、接口响应不稳定就开始猜测和乱改配置。把缓存体系拆开来看,按层次设计并配合发布流程,就能把大部分“玄学”变成可控的工程问题。下面把一个完整的网页缓存系统拆成可操作的模块,给出实践建议、常见坑和调试方法,方便你直接落地在 91 网页版或任意类似架构上。
一、先说清楚:缓存的几条基本判断逻辑
- 缓存分层:浏览器缓存(客户端)、CDN/边缘缓存、反向代理(如 Nginx/Varnish)、应用层缓存(Redis/Memcached)、数据库缓存/查询结果缓存。
- 静态资源与动态 HTML/接口要分开策略:静态资源可以长期缓存并通过文件名版本控制;HTML/接口大多数需要短缓存或条件缓存以保证数据及时性。
- 两种无痛更新方式:文件名内容哈希(长期缓存)和短缓存 + CDN 清除(动态内容或页面)。
二、浏览器端:头部与服务工作线程
- 常见响应头:
- Cache-Control: public/private, max-age=SECONDS, no-cache, no-store, must-revalidate, immutable
- ETag / Last-Modified:条件请求(304)机制
- Vary:告诉缓存根据哪些请求头区分缓存条目(如 User-Agent、Accept-Encoding、Cookie)
- 推荐做法:
- 静态资源(带内容哈希文件名):Cache-Control: public, max-age=31536000, immutable
- HTML 页面(入口页 index.html):Cache-Control: no-cache 或 max-age=0, must-revalidate。让浏览器每次发起条件请求以保证及时加载新的 index.html(从而引用新哈希资源)。
- 动态接口:除非可确定为可缓存(例如热点配置、公共数据),通常使用 Cache-Control: private, max-age=0, must-revalidate 或短缓存。
- Service Worker:适合离线或复杂策略,但要避免与 HTTP 缓存冲突。Service Worker 可以用来做 fine-grained cache-first / network-first 策略,但上手要小心版本迭代与清理。
三、CDN / 边缘缓存:差异化的控制权
- CDN 的基本职责是减小原站压力并加速分发。CDN 会优先参考 Origin 的 Cache-Control,但很多 CDN 支持 override(edge TTL)与 surrogate headers(Surrogate-Control)。
- 常用特性:
- Edge TTL:在边缘缓存一个对象的时间
- Stale-while-revalidate / stale-if-error:允许边缘在过期后继续返回旧数据并后端异步刷新或在后端异常时回退
- 缓存 Key:是否包含 querystring,是否区分大小写,是否包含 Cookie / header
- 推荐:
- 静态资源走 CDN,使用内容哈希 + 长 TTL
- HTML 和需要快速迭代的接口:设置较短的 edge TTL 或让 CDN 尊重 origin 的短缓存头;在发布时配合 CDN 的 Cache Purge 或 Surrogate-Key 批量失效
- 使用 stale-while-revalidate 来平衡延迟与实时性(例如 API max-age=30, stale-while-revalidate=60)
四、反向代理与应用层缓存
- 反向代理(Nginx, Varnish)常常作为边缘或内网缓存:
- 可以做更细致的缓存 key 策略、请求合并(coalescing)和后端健康检查
- Varnish 配合 VCL 可以实现按 header、cookie 选择缓存或绕过缓存
- 应用层缓存(Redis / Memcached):
- 把热点数据或计算成本高的结果缓存到 Redis,设置合适 TTL,并用命名空间和前缀管理版本
- 设计缓存 key:使用有语义的前缀、参数序列化确保唯一且可控,如 user:{id}:profile:v2
- 缓存雪崩 / 缓存击穿 / 缓存穿透防护:
- 雪崩:避免大量 key 同时过期,使用 TTL 随机抖动或分散过期时间
- 击穿:对热点 key 加锁(singleflight / mutex)做单一重建,或用永不过期的空占位加短 TTL 复试
- 穿透:对不存在数据加入短期缓存空值,或校验请求合法性
五、版本管理与发布策略(避免“文件不更新”)
- 静态资源使用内容哈希(content-hash)文件名,配合 CDN 长缓存,能彻底避免浏览器读旧文件。
- HTML 入口文件不做长期缓存:让它指向最新哈希资源;发布时把 index.html 扔到 CDN 并短期缓存,发布后可以选择立即刷新 CDN。
- 可采用蓝绿/灰度发布:先发布到一部分边缘节点或用户组,观察缓存行为与回退路径。
- Surrogate-Key / Cache-Tags:一些 CDN(Fastly、Cloudflare Workers + Cache API 等)支持标签式失效,可以在改动时按 tag 批量清除相关缓存,比按 URL 更方便。
六、强制刷新与清除(运营/开发常用)
- 三种常见方法:
- 文件名变更(推荐,最稳):改哈希并更新 HTML。客户端自动获取新资源。
- CDN API/控制台 Purge:发布后调用清除接口,注意大规模 purge 有成本或限速。
- 控制 Cache-Control:在短时间内把 HTML 改为 no-cache,后续再改回(操作需谨慎,可能影响性能)。
- 实操建议:把自动化清除纳入 CI/CD 发布流程,记录时间与变更 id,避免手工误删。
七、API 缓存设计实例(给出可直接用的 header 示例)
- 静态文件(js/css/img):
- Cache-Control: public, max-age=31536000, immutable
- index.html:
- Cache-Control: no-cache, max-age=0, must-revalidate
- 公共只读接口(可缓存):
- Cache-Control: public, max-age=60, stale-while-revalidate=30
- 私有用户数据接口:
- Cache-Control: private, max-age=0, must-revalidate
- CDN 边缘 override(若需要):
- Surrogate-Control: max-age=60, stale-while-revalidate=30 注意:ETag 与 Last-Modified 可配合使用,对带有条件请求的接口能显著降低带宽。
八、调试与监控(实用工具与指标)
- 常用工具:
- 浏览器 DevTools — Network 面板查看 response header、age、from cache
- curl -I http://url — 查看响应头
- CDN 提供的边缘日志 / X-Cache / X-Served-By / Age 等自定义头
- tracing:通过在请求中加入请求 id/trace header,观察从边缘到 origin 的路径
- 关键监控指标:
- Cache hit ratio(边缘与应用层分别统计)
- Origin bandwidth & origin request per second(发布前后对比)
- P95/P99 请求时延(注意缓存命中通常极大降低 P95)
- Purge 调用频率与失败率
九、常见坑与对策(经验总结)
- 静态资源更新但用户仍旧看到旧版:
- 检查是否用了不带哈希的 URL 或 index.html 被长期缓存
- 检查 CDN 是否在忽略 querystring 或使用了自定义缓存 key
- Cookie / Authorization 导致缓存未命中:
- 默认情况下,带 Cookie/Authorization 的响应不应被共享缓存;如果需要缓存,需要剥离或用 Vary/不同路径区分
- Vary 设置不当造成缓存爆炸:
- 避免把太多 header 放入 Vary,否则缓存条目指数级增加
- 多层缓存策略冲突(浏览器、CDN、Nginx、应用):
- 明确层级责任:谁主导保鲜(通常 origin 决定,CDN 可 override),在架构文档中写清楚并在 CI/CD里执行
十、落地清单(发布前逐项检查)
- 静态资源是否使用内容哈希并设置 long TTL + immutable
- HTML 入口是否为短缓存或 no-cache
- API 是否按私有/公共分层设置缓存头
- CDN cache key、querystring 策略已测试并记录
- 发布流程包含 CDN purge 或版本切换步骤
- 针对热点 key 已设计防止雪崩与击穿策略
- 监控覆盖 hit ratio、origin load、延迟等指标
结语 缓存不是玄学,而是工程设计:明确每层的职责、用好头部与 CDN 特性、用版本 + 清理策略避免“脏数据”,并把所有动作纳入 CI/CD 自动化与监控体系。按上面这套思路,把 91 网页版的缓存拆开来做,既能保证用户体验和性能,又能把日常发布变成可预测、可回滚的流程。想要我把你当前项目的 headers / CDN 配置做一次诊断?贴出一个典型请求的 response header,我们可以逐项优化。