缓存策略三层模型——CDN、Service Worker 与 HTTP 缓存

文章目录


每日一句正能量

"真正的高贵,不是优于别人,而是优于过去的自己。"

与他人比较("优于别人")是焦虑和迷失的永恒源头,因为它依赖于不可控的外部变量。而与过去的自己比较,则是将人生变为一场可以自我度量、自我掌控的进化游戏。"优于"不一定是功成名就,可以是更平和、更懂得爱、更快速地从挫折中恢复

前言

2026 年 3 月,Codex 官网在一次产品发布活动中经历了流量洪峰------单日 UV 从平日的 8 万飙升至 120 万。令人意外的是,源站服务器的 CPU 使用率始终维持在 15% 以下,数据库连接池从未触达上限,页面平均加载时间反而从 1.2 秒降至 0.4 秒。这一反直觉的表现并非偶然,而是三级缓存架构的系统性胜利:CDN 边缘缓存拦截了 92% 的请求,Service Worker 为回访用户提供了毫秒级响应,浏览器 HTTP 缓存则消除了重复资源的网络往返。本文将完整拆解这套缓存体系的设计原理、配置细节与失效策略。

一、三级缓存架构:从边缘到浏览器的层层拦截

现代 Web 应用的缓存不是单点决策,而是一个分层协作系统。Codex 官网的三级缓存按"距离用户由远及近"排列:

CDN 边缘缓存 是第一道防线,部署在全球 100 余个节点上。当用户请求 /docs/getting-started 时,最近的边缘节点会检查本地缓存。如果命中,直接返回预渲染的 HTML,无需回源。Vercel Edge Network 的实测数据显示,全球 95% 的用户可以在 50ms 内获得边缘缓存响应。

Service Worker 缓存是第二道防线,运行在用户浏览器内部。它拦截所有网络请求,对预缓存的静态资源(JS、CSS、字体)直接返回本地副本,对 API 请求根据策略决定走缓存还是网络。对于回访用户,Service Worker 可以在完全离线的状态下渲染应用壳(App Shell)。

浏览器 HTTP 缓存 是最后一道防线,由 Cache-Control 等响应头控制。它不需要任何 JavaScript 代码,是浏览器原生实现的最高效缓存层。对于哈希化的静态资源(如 main.a3f2b1c.js),浏览器会在一年内直接读取本地磁盘缓存,甚至不发送条件请求验证。

二、HTTP 缓存头:精细化到资源类型的策略

"设置一个长的 max-age"是最常见的缓存误区。不同资源的更新频率、可变性和重要性截然不同,需要差异化的缓存策略。

对于哈希化静态资源(构建时注入内容哈希的文件名),Codex 官网配置:

复制代码
Cache-Control: public, max-age=31536000, immutable

immutable 指令来自 RFC 8246,它明确告知浏览器:这个 URL 对应的资源永远不会改变。即使用户按下强制刷新(Ctrl+F5),浏览器也不会发送 If-None-Match 条件请求------因为它知道服务器端的资源内容与本地缓存完全一致。这一优化将静态资源的重复请求彻底归零。

对于HTML 页面,策略则完全不同。Codex 的文档页使用 ISR(增量静态再生),配置为:

复制代码
Cache-Control: public, max-age=60, stale-while-revalidate=86400

这行指令的含义是:页面在首次生成后缓存 60 秒,60 秒内所有请求直接返回缓存副本;60 秒后,缓存进入"陈旧但可用"状态,边缘节点仍然立即返回缓存副本,但同时在后台向源站发起重新验证请求;如果源站生成了新版本,下一次请求将获得更新后的内容。用户在整个过程中始终体验到亚秒级响应,而内容的"新鲜度"误差被控制在 60 秒以内。

三、Service Worker:离线可用与智能拦截

Service Worker 是浏览器与网络之间的可编程代理。Codex 官网使用 Workbox 8.x(Google Chrome Aurora 团队维护)自动生成和管理 Service Worker,避免手写 fetch 事件的繁琐与易错。

预缓存策略 在 Service Worker 安装阶段自动完成。构建工具(Next.js / Vite)会生成一份带内容哈希的资源清单(__WB_MANIFEST),Workbox 在 install 事件中批量下载这些资源存入 CacheStorage。这意味着用户在首次访问时,除了当前页面所需的资源,Service Worker 还在后台静默缓存了应用壳和其他关键路由的依赖------为后续导航的瞬时加载打下基础。

ts 复制代码
// sw.ts
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';

// 自动预缓存构建产物(由构建工具注入清单)
precacheAndRoute(self.__WB_MANIFEST);

// 图片资源:CacheFirst,缓存 30 天
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 100,
        maxAgeSeconds: 30 * 24 * 60 * 60,
      }),
    ],
  })
);

// API 请求:NetworkFirst,10 秒超时后回退缓存
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api-cache',
    networkTimeoutSeconds: 10,
    plugins: [
      new ExpirationPlugin({
        maxEntries: 50,
        maxAgeSeconds: 5 * 60,
      }),
    ],
  })
);

// 文档内容:StaleWhileRevalidate,即时响应 + 后台刷新
registerRoute(
  ({ url }) => url.pathname.startsWith('/docs/'),
  new StaleWhileRevalidate({
    cacheName: 'docs-content',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 200,
        maxAgeSeconds: 24 * 60 * 60,
      }),
    ],
  })
);

离线兜底是 Service Worker 的另一项核心能力。当用户处于完全离线状态时,对未缓存的路由请求会返回预置的离线页面:

ts 复制代码
import { setCatchHandler } from 'workbox-routing';

setCatchHandler(async ({ request }) => {
  // 对导航请求返回离线页面
  if (request.destination === 'document') {
    return caches.match('/offline.html');
  }
  return Response.error();
});

四、ISR:增量静态再生的工程实践

Next.js 的 ISR(Incremental Static Regeneration)是连接 CDN 缓存与源站渲染的桥梁。它解决了静态站点生成(SSG)的核心矛盾:构建时预渲染保证了速度,但内容更新需要重新构建整个站点;服务端渲染(SSR)保证了实时性,但每个请求都消耗服务器资源。

ISR 的折中方案是"首次请求时渲染并缓存,后续请求返回缓存,后台定期重新验证"。Codex 官网的文档页配置如下:

tsx 复制代码
// app/docs/[slug]/page.tsx
export const revalidate = 60; // 60 秒后后台重新验证

export async function generateStaticParams() {
  // 构建时预渲染热门页面
  const slugs = await getPopularDocSlugs();
  return slugs.map((slug) => ({ slug }));
}

export default async function DocPage({ params }: { params: { slug: string } }) {
  const doc = await fetchDoc(params.slug);
  return <DocContent doc={doc} />;
}

generateStaticParams 在构建时生成热门文档的静态页面,确保首访用户也能获得 CDN 缓存响应。对于长尾文档(如冷门 API 参考页),首次请求会触发服务端渲染,结果存入 CDN 缓存,后续访问即享受静态性能。

revalidate = 60 是 Codex 在"新鲜度"与"服务器负载"之间的平衡选择。对于文档内容,60 秒的延迟是可接受的;对于博客文章,这个值放宽到 600 秒;对于定价页等极少变化的内容,则使用 86400 秒(24 小时)。

2026 年的 Next.js 16 还引入了 on-demand revalidation (按需重新验证),允许在 CMS 内容更新时主动触发缓存失效,而非被动等待 revalidate 超时:

ts 复制代码
// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache';

export async function POST(request: Request) {
  const { path, secret } = await request.json();
  if (secret !== process.env.REVALIDATE_SECRET) {
    return Response.json({ error: 'Invalid secret' }, { status: 401 });
  }
  revalidatePath(path);
  return Response.json({ revalidated: true });
}

五、缓存失效:版本控制与清理策略

缓存的最大敌人不是未命中,而是"陈旧命中"------用户看到了过期的内容却毫不知情。Codex 官网通过三层机制确保缓存可控可失效:

文件名哈希 是静态资源缓存的基石。Next.js 在构建时为每个 JS/CSS 文件注入内容哈希(如 main.a3f2b1c.js),文件内容变化即哈希变化,URL 随之变化。旧版本的缓存自然失效,因为没有任何页面再引用旧的 URL。这是唯一一种"零成本、零风险"的缓存失效方式。

Service Worker 版本管理 通过 skipWaiting()clients.claim() 实现即时更新。当新版本部署时,新的 Service Worker 进入 waiting 状态,旧版本继续服务当前页面。Codex 在应用壳中监听 waiting 事件,提示用户刷新以获取更新:

ts 复制代码
// 客户端注册逻辑
const wb = new Workbox('/service-worker.js');

wb.addEventListener('waiting', () => {
  // 显示"新版本可用,点击刷新"提示
  showUpdateNotification(() => {
    wb.messageSkipWaiting();
    wb.addEventListener('controlling', () => window.location.reload());
  });
});

wb.register();

CDN 缓存标签 用于按需批量失效。Codex 为每个文档页面附加缓存标签(如 doc:react-hookscategory:frontend),当某类内容批量更新时,通过 API 调用一次性清除所有匹配标签的缓存,而非逐个 URL 清除。

六、完整配置:Next.js 缓存优化集成

以下是 Codex 官网的 next.config.ts 缓存相关配置,整合了 HTTP 头、ISR 与静态资源策略:

ts 复制代码
// next.config.ts
import type { NextConfig } from 'next';

const config: NextConfig = {
  // ISR 全局默认(可被页面级覆盖)
  experimental: {
    staleTimes: {
      dynamic: 30,
      static: 300,
    },
  },

  // 自定义 HTTP 响应头
  headers: async () => [
    {
      source: '/_next/static/:path*',
      headers: [
        {
          key: 'Cache-Control',
          value: 'public, max-age=31536000, immutable',
        },
      ],
    },
    {
      source: '/:path*',
      headers: [
        {
          key: 'Cache-Control',
          value: 'public, max-age=60, stale-while-revalidate=86400',
        },
      ],
    },
  ],

  // 图片优化缓存
  images: {
    minimumCacheTTL: 60 * 60 * 24 * 30, // 30 天
    formats: ['image/avif', 'image/webp'],
  },
};

export default config;

结语

缓存策略的设计本质是"在数据新鲜度与系统性能之间寻找最优平衡"。Codex 官网的三级缓存架构证明,当 CDN 边缘缓存、Service Worker 本地缓存和浏览器 HTTP 缓存协同工作时,系统可以在承受 15 倍流量洪峰的同时保持亚秒级响应。关键不在于设置最长的 max-age,而在于为每种资源类型匹配最合适的策略:哈希资源用 immutable,动态页面用 stale-while-revalidate,API 响应用 NetworkFirst,离线场景用 CacheFirst。在下一篇文章中,我们将探讨前端监控体系------从性能埋点到错误追踪的完整可观测性方案。


转载自:https://blog.csdn.net/sghtgjfhv/article/details/164166926

欢迎 👍点赞✍评论⭐收藏,欢迎指正

相关推荐
tg_xianheyun3 天前
BytePlus CDN加速如何优化海外访问体验?
云计算·cdn·cdn加速·云直播·byteplus
tg_xianheyun3 天前
2026BytePlus CDN加速应用场景全面解析
服务器·数据库·阿里云·云计算·cdn·全球访问优化·云直播
却道天凉_好个秋10 天前
计算机网络:CDN
学习·音视频·cdn
juxieyiyi87810 天前
摆脱第三方分成,搭建专属算力管理平台
cdn·pcdn·互联网项目·双收益·pcdn平台搭建双收益
霸刀12 天前
一次“Google 能打开,FlexTV 打不开”的 DNS 故障排查:从 curl 超时到完整证据链
mac·cdn·dns
霸刀12 天前
nslookup 与 dig 使用指南:DNS 查询、区别对比及真实案例解读
linux·cdn·dns
sudebao点com13 天前
一次“Google 能打开,FlexTV 打不开”的 DNS 故障排查:从 curl 超时到完整证据链
windows·macos·https·cdn·dns·nslookup·网络排错
川川菜鸟14 天前
宝塔 Nginx 构建 Service Worker
service worker·网站容灾·nging
黄俊懿21 天前
【架构师从入门到进阶】第五章:DNS&CDN&网关优化思路——第三节:CDN扩展-多地址直连
网络·计算机网络·架构·系统架构·cdn·dns·架构设计