每天 90 亿次请求:cdnjs 如何迁移到 Cloudflare Developer Platform

2026-07-30 21 预计阅读时间: 1 分钟
来源: blog.cloudflare.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

Cloudflare 将 cdnjs 完整迁移到了自己的 Developer Platform。这个开源 CDN 每天处理约 90 亿次请求,因此这不只是一次内部“吃自己的狗粮”,更是对 Workers、Workflows 及整个平台容量边界的真实压力测试。迁移过程中暴露出的限制,也推动了平台配额和能力的提升,并让所有开发者受益。

90 亿次请求改变了迁移问题的尺度

把 90 亿除以一天的 86400 秒,平均吞吐量约为每秒 10.4 万次请求。实际流量不会均匀分布,峰值、热门库发布和区域性访问都会制造更高压力。

在这个尺度上,迁移不能只验证“请求能否返回 200”。工程团队需要同时关注:

  • 缓存命中率:少量命中率下降也会把大量请求压向源站。
  • 尾延迟:平均延迟正常,并不代表 P95、P99 或跨区域访问正常。
  • 缓存键稳定性:查询参数、大小写或路径归一化错误都可能产生重复缓存。
  • 回源保护:缓存冷启动或热门文件失效时,必须避免请求集中冲击存储层。
  • 自动化任务可靠性:库索引、元数据更新和发布流程需要可重试、可追踪,并保持幂等。

这也是 cdnjs 对 Developer Platform 的价值所在:它用生产流量检验平台构件是否真的能够承载互联网级公共服务,而不是只在小型示例中工作。

Workers 处理请求,Workflows 承接长流程

从平台职责来看,可以把这类 CDN 拆成两条路径。

请求路径对延迟极度敏感,适合由 Workers 在边缘完成 URL 校验、缓存查询、响应头设置和必要的回源。任何多余网络调用都会被放大,因此热路径应保持短小、确定且可观测。

控制路径则处理库发布、索引构建、元数据同步和失败重试。这类工作可能跨越多个步骤,不应绑在单次 HTTP 请求的执行时间内。Workflows 的意义在于持久化步骤状态,并为长时间任务提供重试和恢复边界。

来源摘要没有披露 cdnjs 的具体组件图、存储选择或缓存规则,因此不能把某一种示例架构当作其实际实现。更稳妥的理解是:迁移把一个高流量 CDN 放到了 Cloudflare 自己提供的开发构件上,并以实际负载推动了 Workers 和 Workflows 限制的提升。

可以这样实践:构造一个最小边缘 CDN Worker

下面是一个可改造的最小示例。它限制只代理静态资源扩展名,先读取 Cloudflare 缓存,未命中时再访问源站。运行前需要安装 Node.js,并把 ASSET_ORIGIN 改成自己的 HTTPS 静态资源源站。

创建 package.json

{
  "name": "edge-cdn-worker",
  "private": true,
  "scripts": {
    "dev": "wrangler dev",
    "deploy": "wrangler deploy"
  },
  "devDependencies": {
    "wrangler": "^4.0.0"
  }
}

创建 wrangler.toml

name = "edge-cdn-worker"
main = "src/index.js"
compatibility_date = "2025-01-01"

[vars]
ASSET_ORIGIN = "https://static.example.com"

创建 src/index.js

const ALLOWED_EXTENSIONS = /\.(css|js|json|map|svg|png|jpg|jpeg|webp|woff2?)$/i;

export default {
  async fetch(request, env, ctx) {
    if (request.method !== "GET" && request.method !== "HEAD") {
      return new Response("Method Not Allowed", { status: 405 });
    }

    const incoming = new URL(request.url);
    let pathname;

    try {
      pathname = decodeURIComponent(incoming.pathname);
    } catch {
      return new Response("Bad Request", { status: 400 });
    }

    if (pathname.includes("..") || !ALLOWED_EXTENSIONS.test(pathname)) {
      return new Response("Not Found", { status: 404 });
    }

    const cacheKey = new Request(incoming.origin + incoming.pathname, {
      method: "GET"
    });
    const cache = caches.default;
    const cached = await cache.match(cacheKey);

    if (cached) {
      const response = new Response(cached.body, cached);
      response.headers.set("X-Edge-Cache", "HIT");
      return response;
    }

    const origin = new URL(env.ASSET_ORIGIN);
    origin.pathname = incoming.pathname;
    origin.search = "";

    const upstream = await fetch(origin, {
      headers: { Accept: request.headers.get("Accept") || "*/*" }
    });

    if (!upstream.ok) {
      return new Response("Asset unavailable", { status: upstream.status });
    }

    const response = new Response(upstream.body, upstream);
    response.headers.set("Cache-Control", "public, max-age=300, s-maxage=86400");
    response.headers.set("X-Content-Type-Options", "nosniff");
    response.headers.set("X-Edge-Cache", "MISS");

    if (request.method === "GET") {
      ctx.waitUntil(cache.put(cacheKey, response.clone()));
    }

    return response;
  }
};

安装并本地运行:

npm install
npm run dev
curl -i http://localhost:8787/library/example.min.js

登录 Cloudflare 后可以部署:

npx wrangler login
npm run deploy

这个示例用于展示请求路径,不等同于 cdnjs 的实现。生产环境还要补充源站身份验证、内容完整性校验、请求合并、分层缓存、速率限制、结构化日志和告警。对于带版本号且内容不可变的文件,还可以使用更长的缓存周期;可变路径则应采用较短 TTL 或显式失效机制。

大规模迁移应按指标推进

迁移公共 CDN 时,最危险的做法是一次性切换全部流量。可以先建立旧系统与新系统的响应对比,再按域名、地区或流量比例逐步放量。每个阶段至少应检查状态码分布、缓存命中率、回源请求量、P95/P99 延迟和字节一致性。

上线前可以使用这份检查表:

  • 缓存键是否明确处理查询参数、压缩格式和请求头。
  • Worker 故障时是否有回退路径,源站是否具备限流保护。
  • 发布与索引任务是否幂等,重试是否会产生重复数据。
  • 日志采样是否足以定位问题,又不会产生失控的成本。
  • 平台 CPU、请求体、子请求和工作流配额是否覆盖峰值,而不只是日均值。
  • 是否准备了按区域和按比例回滚的开关。

cdnjs 的迁移说明了一个关键事实:平台能力只有在真实、持续且高并发的负载下才会显露边界。对普通团队而言,最值得借鉴的并非直接复制某个架构,而是把边缘请求路径、后台长流程和渐进式迁移分别设计,并用生产指标决定下一步。


相关推荐