返回博客

OpenNext 的区域缓存如何减少每次缓存命中时的 Workers CPU 占用

2026-04-254 min read

Next.js 默认是为在 Vercel 上运行而构建的。它需要文件系统、图像优化服务和特定的运行时模型。要在其他地方运行 Next.js,例如 Cloudflare Workers,您需要一个适配器来将这些需求桥接到目标平台的原始功能上。

OpenNext 就是这样一个开源项目。它最初是 AWS Lambda 的 Next.js 适配器,现在也为 Cloudflare 和 Netlify 提供了官方适配器。我们这里关注的包是 @opennextjs/cloudflare,它允许您将 Next.js 应用部署到 Cloudflare Workers,并将 Next.js 的缓存层连接到 Cloudflare 的存储原始功能。

如果您遵循了 OpenNext 推荐的 Cloudflare 设置,您的 open-next.config.ts 会连接其中两个原始功能。Workers KV 存储预渲染页面的缓存 HTML。D1 存储 revalidateTag() 写入的缓存标签元数据。(默认情况下,defineCloudflareConfig() 使用 "dummy" 的无操作缓存;KV 和 D1 是您自己连接的推荐替代方案。)

KV 和 D1 都是远程服务。每次请求都需要消耗真实的 CPU,即使页面几天没有更改。withRegionalCache 就是修复这个问题的包装器。

它是什么

withRegionalCache 作为覆盖项随 @opennextjs/cloudflare 一起提供,您可以将其包装在现有的增量缓存实现周围。在 open-next.config.ts 中,这只需要导入一个文件并进行一次包装更改。

import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache";
import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache";
import d1TagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache";

export default defineCloudflareConfig({
incrementalCache: withRegionalCache(kvIncrementalCache, {
mode: "long-lived",
bypassTagCacheOnCacheHit: true,
}),
tagCache: d1TagCache,
});

如果您选择的后端是 r2IncrementalCachestaticAssetsIncrementalCache,它也能以同样的方式工作。该包装器独立于您使用的底层缓存。

它做什么

两件事,都值得理解。

1. KV 前面的 Workers Cache API 层

第一个行为是使用 Workers Cache API caches.default 的区域缓存层。Cache API 仅限于每个 Cloudflare 数据中心。它不是全局复制的,但这正是重点所在。从 caches.default 读取在 CPU 方面几乎是免费的,并且在 1 到 5 毫秒内完成。

因此,当请求到达缓存的页面时,Worker 会首先检查本地数据中心的 Cache API。如果条目存在,它会立即返回。如果不存在,它会回退到 KV(全局约 30 到 100 毫秒的读取时间),为下次填充本地 Cache API,然后返回响应。

mode: "long-lived" 选项默认将区域条目保留长达 30 分钟,这对于 ISR/SSG 页面效果很好。

2. 可选的 D1 标签缓存命中旁路

第二个行为是选择加入的:bypassTagCacheOnCacheHit: true

默认情况下,每次请求缓存的页面时,还会查询 D1 以检查该页面的任何重新验证标签是否已被失效。D1 读取需要大约 30 到 200 毫秒的实际时间,并在查询设置和结果解析时消耗 CPU。

启用旁路后,Worker 会在缓存命中时跳过 D1 的往返。权衡是:如果您调用 revalidateTag(),区域缓存可能会继续提供页面的旧版本,直到其区域条目过期。对于不随时间频繁更改的内容,这对用户来说是不可见的。

它如何提供帮助

没有包装器的情况下,典型的“缓存命中”在 Worker 上看起来是这样的:

  1. 唤醒隔离区。
  2. 查询 D1 以获取此 URL 的标签失效信息。
  3. 从 KV 读取缓存的 HTML。
  4. 构建响应并返回。

每次请求都有两次远程往返和有意义的 CPU 工作。

使用包装器后:

  1. 唤醒隔离区。
  2. 从本地 Cache API 读取。
  3. 返回。

从用户的角度来看,结果相同。Worker 的工作量大大减少。

在一个小型内容网站上,每天大约处理 33,000 次请求,启用此功能后,每次请求的平均 CPU 时间从 668 毫秒降至约 40 毫秒,减少了约 94%。计入 Cloudflare 每百万 CPU 毫秒 0.02 美元的总 CPU 时间从每天约 2200 万毫秒降至每天约 130 万毫秒。计费仪表板中的成本曲线形状在部署当天就发生了变化。

节省的成本与流量成线性比例。网站越繁忙,“每次请求两次远程往返”和“每次请求一次本地 Cache API 读取”之间的差距就越大。

何时使用它

如果您的 OpenNext 配置正在使用 kvIncrementalCached1TagCache(内容网站在 Cloudflare 上的默认推荐设置),请启用 withRegionalCache 并设置 bypassTagCacheOnCacheHit: true。博客、文档网站、营销页面以及类似的静态内容工作负载可以立即受益,并且过时性权衡对用户来说是不可见的。

如果您运行的是对实时新鲜度要求很高的应用程序(例如定价、库存、聊天),请将 bypassTagCacheOnCacheHit 设置为 false。您仍然可以获得区域 Cache API 层、更快的 KV 读取、更低的每次请求 CPU 使用率,而没有过时窗口。

这是一个五行代码的更改。这种更改在第一天就能收回成本。

保持更新

将最新文章和见解发送到您的收件箱。

Unsubscribe anytime. No spam, ever.