Agent Developer Roadmap
JavaScript 运行时

Edge Runtime:Isolate、分布式请求与平台配额

核验日期:2026-07-16。本文以 Cloudflare Workers 为代表解释 Edge Runtime,不把所有边缘平台视为同一实现。配额、API 和 compatibility flags 更新频繁;数值仅反映核验日官方文档,部署前必须重新检查 Workers Limits 和目标套餐。

Edge Runtime 的决定性变化在执行单位、生命周期和宿主能力,“服务器离用户更近”只是结果之一。传统 Node 服务通常以长寿命进程为中心,拥有文件系统、端口、进程信号和连接池;Cloudflare Workers 以事件驱动的 V8 Isolate 为中心,平台在全球节点按请求路由,实例可复用也可驱逐,能力通过 Web APIs 与 bindings 暴露,并受每请求 CPU、内存、子请求、连接和 bundle 等限制。

学习结果与适用边界

完成本文后应能:

  • 解释进程、容器、VM、V8 Isolate 和请求 invocation 的关系,不把 isolate 称为线程或完整 VM。
  • 从边缘入口、模块初始化、fetch handler、subrequest、stream、waitUntil 到日志追踪一次请求。
  • 说明全局变量可复用但不持久,实例可能并发处理多个请求,任何两个请求不保证到同一实例。
  • 根据 CPU/wall time、128 MB isolate 内存、subrequest/connection、bundle/startup 等平台限制设计算法。
  • 区分 Web API、Node compatibility、Wrangler polyfill/stub、平台 binding 和普通 npm package。
  • 判断认证前置、重定向、缓存、轻量聚合适合边缘,而重 CPU、长事务、任意文件/进程/native addon 不适合。
  • 使用 compatibility date 管理运行时行为升级,并保留回归和回滚。

Isolate 模型

How Workers works说明 Cloudflare 在一个 runtime 进程中运行许多 V8 isolates。每个 isolate 有隔离的内存上下文,比为每段代码启动完整 VM/进程轻;平台可快速创建、复用和驱逐。隔离仍依赖 runtime 与平台安全工程,不应将其简化为“V8 自己解决所有多租户风险”。

一个 isolate 可处理多个请求,await 外部 I/O 时同一事件循环可能推进其他请求。模块顶层常量和只读已解析数据可缓存;可变用户状态不能依赖全局,因为并发会交错、部署有多个位置/实例、平台可驱逐。即便某次压测总命中同一实例,也不能推出生产路由粘性。

“无冷启动”应怎样理解

Isolate 相比容器/进程显著轻,官方描述其启动非常快。但应用仍有 bundle 解析、模块求值、Wasm 编译、连接建立、cache miss 与地区首次执行,用户可以观察到冷/热差异。性能报告应把平台调度、模块初始化和上游连接分开,不用绝对的“零冷启动”。

一次请求的端到端流程

平台把标准 Request、bindings env 和 execution context ctx 交给 module worker 的 fetch。handler 返回 Response 后 body 可能继续流式发送;ctx.waitUntil() 可登记响应后的工作,但它有生命周期上限,并非 durable job queue。计费/限制可能区分 CPU time 与 wall time;等待网络通常不计 CPU,但连接、subrequest 和 duration 仍有限制。

Module Worker 与 bindings

interface Env {
  CATALOG: KVNamespace;
  ORIGIN: Fetcher;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);
    if (request.method !== "GET") {
      return Response.json({ code: "METHOD_NOT_ALLOWED" }, {
        status: 405,
        headers: { allow: "GET" },
      });
    }
    if (url.pathname !== "/technologies") {
      return Response.json({ code: "NOT_FOUND" }, { status: 404 });
    }

    const cacheKey = new Request(url.origin + url.pathname + url.search, request);
    const cached = await caches.default.match(cacheKey);
    if (cached) return cached;

    const response = await env.ORIGIN.fetch(request);
    if (response.ok) ctx.waitUntil(caches.default.put(cacheKey, response.clone()));
    return response;
  },
} satisfies ExportedHandler<Env>;

这是边界示例,不是完整缓存策略。Service Binding 避免经公网调用同账户 Worker;Cache API 行为、可用位置和 Set-Cookie/Vary 等需核对官方文档。response.clone() 可能增加流/内存压力,必须对大响应测试。写 cache 放在 waitUntil 中意味着请求成功不保证 cache 写一定成功,缓存只能是可重建优化。

bindings 是平台注入的 capability:KV、R2、D1、Durable Objects、Service Bindings、secrets 等。相较从全局 env 读取字符串,它能把资源与部署配置关联,但仍要限制权限、环境和目标。TypeScript interface 只做静态描述,错误/缺失 binding 需要部署校验。

生命周期:全局缓存可以,持久状态不可以

模块顶层在 isolate 初始化时执行,适合创建 immutable lookup、解析 schema、准备 client wrapper;不要每请求重复大初始化。全局可变 Map 可做有界 opportunistic cache,但必须满足:丢失不影响正确性;并发读写安全;有容量/TTL;不含跨用户 secret;部署版本切换能重建。

const cache = new Map<string, { expires: number; value: string }>();

function getFresh(key: string, now = Date.now()) {
  const entry = cache.get(key);
  if (!entry || entry.expires <= now) {
    cache.delete(key);
    return undefined;
  }
  return entry.value;
}

这是每 isolate 的 best-effort cache,不是全局一致缓存。不能用它保存余额、锁、幂等记录或登录 session。需要单对象串行状态时评估 Durable Objects;需要全局低延迟读但可接受最终一致时评估 KV;关系查询/事务评估 D1/区域数据库。具体一致性和配额属于平台数据设计,不能从 API 名推断。

CPU time、wall time 与并发

核验日 Workers Limits列出:Free HTTP invocation CPU 10 ms;Paid 默认 30 秒、可配置最高 5 分钟;每 isolate 128 MB 内存;Free/Paid subrequest 与 bundle 等限制不同。HTTP wall duration 在客户端连接保持时没有同样的固定硬上限,但响应结束/客户端断开后未登记工作可能取消,waitUntil 也有有限延长窗口。

这些数字会变,架构文档应记录“依赖哪类限制”和核验链接,而不是把数值散落为永久常量。

资源计量含义容易误解设计动作
CPU timeisolate 实际执行代码的 CPUawait fetch 的等待等同 CPUprofile JSON/crypto/SSR,拆重计算
wall timeinvocation 从开始到结束无 HTTP duration limit 等于永久后台进程客户端、更新、waitUntil 都会结束工作
memoryisolate heap + Wasm 等每请求独享 128 MB同 isolate 多请求,流式与有界缓存
subrequestsfetch/平台服务调用次数一行 fetch 永远只计一次redirect 与内部调用也可能计数
connections同 invocation 等待响应头的连接无限 fan-out 会自动排队有界并发、批量接口、取消未用 body
bundle/startup部署代码与启动预算bundler 成功就能部署查动态依赖、Wasm、模块顶层工作

CPU 超限可返回 Cloudflare 1102,日志 outcome 为 exceededCpu;memory 超限也可能 1102 但 outcome 不同。排障必须查看平台 invocation outcome、CPU/wall 和内存,不能只看客户端同一状态页。

Fan-out 的隐藏成本

边缘聚合十个上游似乎减少客户端 RTT,却增加 subrequests、并发连接、最慢上游尾延迟和失败组合。使用有界并发、共同 deadline、每上游预算和部分失败策略。可由源站批量接口解决时,不在 Edge 写复杂 fan-out orchestrator。

Streams 与内存

代理大文件时把上游 Response.body 直接流向下游,避免 arrayBuffer/text/json 全量缓存。TransformStream 只做逐 chunk 转换,并尊重 backpressure。某些操作如签名、解压或 HTML 改写可能隐式缓存,结合 128 MB isolate limit 测试最坏 body。

async function proxy(request: Request): Promise<Response> {
  const upstream = await fetch("https://origin.example.com/large", {
    signal: request.signal,
  });
  return new Response(upstream.body, {
    status: upstream.status,
    headers: upstream.headers,
  });
}

真实代理不能原样转发所有 headers;需过滤 hop-by-hop、安全与缓存相关字段,重新验证 redirect 和目标 host,设置业务 deadline。客户端断开 signal 是否传播到所有平台/上游路径应集成测试。

Node.js compatibility:native、polyfill 与 stub

Workers 以 Web APIs 为首要宿主,同时提供 Node compatibility。启用 nodejs_compat 且 compatibility date 达到要求时,runtime 原生支持部分 Node APIs;Wrangler 可用 unenv 注入尚未原生实现的 polyfill。官方文档明确有些 mocked method 会 noop 或在调用时抛出“not implemented”。因此 import 成功不是功能成功。

{
  "name": "runtime-catalog-edge",
  "main": "src/index.ts",
  "compatibility_date": "2026-07-16",
  "compatibility_flags": ["nodejs_compat"]
}

配置日期仅为本文核验示例;项目应选择经过回归的日期,不自动每天推进。Node 包评估顺序:检查是否依赖文件/进程/native addon;解析实际 bundle;在本地 Workers runtime 运行功能 fixture;部署 staging 运行;查看是否包含 stub;验证内存/CPU/stream;最后才认为兼容。

以下能力通常是硬风险:任意 fs 路径、监听自定义端口、child_process 真执行、native .node addon、V8 inspector/heap snapshot、OS signal/daemon、长期本地 socket pool。兼容 flag 页面可能为模块提供 stub 以便导入,stub 不能创造平台没有的进程能力。

Compatibility date:运行时行为的版本锁

Cloudflare 持续更新 Workers runtime。compatibility date 让项目选择截至某日期的行为,flags 可提前启用或临时保持旧行为。这比永久冻结 runtime 更安全,也意味着升级日期是需要评审的代码/配置变更。

测试范围包括 URL/Headers/Streams、Node APIs、module loader、WebSocket、cache、encoding 和错误类。升级 Wrangler/bundler 与 compatibility date 尽量分开,避免两个变量同时变化。记录旧/新日期、变更链接和回滚截止。

分布式执行与状态

请求可落到任意位置,两个连续请求不保证同 region/isolate。把状态放在单一中心数据库会让 edge compute 离用户近、数据却远,增加 backhaul latency。Smart Placement 等平台能力可能让计算靠近数据,但选择要以业务 trace 验证。

状态类型合理位置关键语义
公开静态/可缓存响应CDN Cache/Cache APIcache key、TTL、purge、stale
配置/小型低频读KV 或配置 binding最终一致、版本、失效
大对象R2/object storagestreaming、range、metadata
关系数据D1/外部数据库transaction、region、connection
单实体协调Durable Objectobject id、serialization、迁移
request-scoped 临时值handler local不跨请求
opportunistic cacheisolate global可丢失、有界、非权威

Edge Runtime 不能充当分布式数据库。写入、幂等、冲突和一致性会在全栈后端部分进一步设计;当前只需明确 isolate global 不具备持久化语义。

Cache:正确性先于命中率

Cache key 至少考虑 URL、method、内容协商、认证范围、tenant、版本和会影响响应的 headers。私有响应不得因遗漏用户/组织维度进入共享 cache。VarySet-Cookie、authorization 与平台缓存规则需核对。

缓存更新采用版本化 key 或明确 purge;stale-while-revalidate 可降低尾延迟,但后台更新失败时要可观察。waitUntil(cache.put(...)) 不应承载唯一业务写。缓存击穿通过 coalescing、TTL jitter 或源站保护处理,但 Durable coordination 有成本,不为低流量过度设计。

安全边界

Edge 位于公网最前面,适合做输入大小、method、host、认证 token 初筛和速率策略,但不能成为唯一授权来源。源站必须验证来自 Edge 的身份或只通过 Service Binding/私网可达,防绕过。

Secret 与 bindings

secret 通过平台 secret binding 注入,不写入 wrangler 明文或 bundle。日志不打印 env/binding。不同环境使用不同资源,preview/staging 不共享生产写权限。Worker bundle 和 Source Map 可能暴露代码结构,发布策略应明确。

SSRF 与代理

不要把 query 参数直接传给 fetch。解析 URL 后 allowlist scheme/host/port,禁 loopback/private/metadata,限制 redirect 并对每次跳转重新验证。Service Binding 优先于公网 URL。HTTP header 注入、cache poisoning、request smuggling 还要结合平台规范测试。

不可信代码

租户间 Isolate 是平台实现;在自己的一个 Worker 内用 eval 执行用户代码不会自动得到新的租户隔离和独立配额。使用 Workers for Platforms/专门 sandbox 产品或 VM 级环境,并阅读具体安全模型。

后台工作与 waitUntil

ctx.waitUntil(promise) 允许响应后继续有限工作,适合日志、cache 写、非关键通知。它不是持久队列:runtime update、异常、时间上限和重试语义都可能影响完成。关键任务写入 Queue/Durable storage 后再返回,并用幂等 consumer 处理。

多个后台任务要独立捕获错误和记录 outcome;一个 rejected Promise 不应让其他清理静默丢失。日志 flush 也受每请求日志量限制,核验日官方限制为每请求 256 KB,不能把 body 全打出。

本地开发与线上差异

Wrangler/Miniflare 提供高保真本地 runtime,但 DNS、路由、真实 bindings、全球位置、资源限制和平台缓存仍有差异。测试分三层:

  1. 纯 handler 单测:构造 Request 和 fake bindings,确定性、快速。
  2. 本地 runtime 集成:模块、Node compat、streams、scheduled/queue 与 limits 近似。
  3. Staging 线上:真实 route、secrets、bindings、compat date、region、logs 和资源 outcome。

不要用 Node 直接调用 handler 作为唯一测试,因为它绕过 Workers runtime。也不要让每个单测访问真实云服务。

性能与放置决策

Edge 的潜在收益来自减少用户到计算的 RTT、在边缘缓存/拒绝请求和减少源站负载。若每次请求仍去单一区域数据库,收益可能只剩 TLS/路由,且多一层 hop。用端到端 trace 分解 user→edge、edge CPU、edge→origin、origin/DB 和 response streaming。

基准至少按多个真实地区测 p50/p95/p99、cache hit/miss、cold/warm、origin distance、CPU、subrequests、errors 和费用。不能拿本地 wrangler dev 与生产 Node region 比。平台宣称 isolate 比进程启动快,是架构事实线索;业务选择仍需应用 bundle/数据位置实验。

适合与不适合

场景适合度条件/替代
URL rewrite、redirect、A/B route规则稳定、可回滚、低 CPU
JWT 基础验证、bot/rate gatekey cache/rotation,源站仍授权
公共内容 cache、轻量个性化cache key 不泄露私有数据
多区域上游选择health/failover 与重试预算
轻量 SSR/API compositionbundle、CPU、subrequest、数据位置通过
大型图片/视频转码用平台专用产品或异步 worker
长 CPU ML 推理低/条件化专用 GPU/容器;Edge 只路由
任意 shell/native addon区域 Node/Bun/Deno/容器
跨多行强事务低/条件化数据库附近后端或 Durable model

故障排查

现象根因候选最小证据修正
本地正常线上 1102CPU/memory resource limitplatform outcome、CPU/wall、memoryprofile/stream/拆任务/调整允许上限
偶发数据回退把 isolate global 当权威状态colo/isolate/版本、持久层记录状态写 durable store,幂等读取
npm 包可 import 但调用报 not implementedWrangler stubbundle 与 stack、compat 文档换 Web/支持 API 或移回区域 runtime
cache 泄露用户数据key 缺少身份/tenant/Varycache key 与请求范围禁共享缓存或包含正确版本/范围
大文件内存失败body 全量 buffer/clonememory outcome、profile、body size端到端 stream,减少 clone/transform
waitUntil 任务丢失把有限后台执行当 durable queuetask id、invocation end、queue record先持久入队,再异步处理
compatibility date 升级后行为变flag/default 改变old/new date contract diff回滚日期,按 changelog 修复再推进

发布、回滚与可观测性

每次发布记录 script version、bundle hash、compatibility date/flags、bindings/environment 和 Wrangler version。日志包含 request id、deployment id、colo(如平台安全允许)、cache status、subrequest count/关键上游、duration、CPU/wall/outcome 与稳定错误码。高基数值不进入 metrics label。

回滚必须同时考虑代码、bindings schema、Durable migrations、compat date 和 cache。旧代码若读不了新数据格式,仅回滚 bundle 不安全。对无状态读取服务,使用独立 route/canary 并保留旧 version 是最小路径。

官方资料

交付检查

完成 Edge 示例时应留下:compatibility date/flags;bundle/部署版本;handler 单测、本地 runtime 与 staging 三层结果;bindings capability 清单;cache key/身份范围;CPU/memory/subrequest/connection 预算;大响应 streaming;waitUntil 非持久边界;Node package native/stub 验证;多地区 hit/miss p95/p99;驱逐/重试/幂等和完整回滚步骤。具备这些证据后,才能说选择的是可运维的边缘执行模型,而不是把区域服务机械搬近用户。

边缘容量演练:让配额进入设计

为一个代表请求写资源账本:解析/认证/序列化各需要多少 CPU;并行和总 subrequest 数;同时等待响应头的连接;最大 request/response body;stream transform 是否缓存;cache clone 数量;日志体积;waitUntil 工作。账本数值来自 staging trace 与 invocation logs,不凭代码行数猜测。

故意把每项推到边界:增加 JSON 大小观察 CPU;让六个以上上游同时慢响应观察连接调度;构造 redirect 链观察 subrequest 计数;慢速发送/读取 body 观察 memory;制造 cache miss fan-out 观察 origin;产生大量日志观察截断。每次只改变一个变量,并核对平台 outcome。演练使用隔离环境和低流量,避免费用与源站冲击。

若容量不足,按机制修正:CPU 高则减少解析/加密/SSR或外置;memory 高则端到端 stream、减少 clone/有界 cache;subrequest 高则批量接口、Service Binding 或预聚合;连接高则有界调度和及时取消 body;日志高则采样与结构化摘要。付费提高上限只有在业务负载合理且成本可接受时使用,不能替代无界算法修复。

多地区放置实验

选择接近用户、接近数据和跨洲三组位置,分别测 cache hit 与 miss。trace 拆出 user→edge、edge CPU、edge→origin、origin/DB。若 miss 的 edge→origin 占主要延迟,考虑 cache、源站多区域、Smart Placement 或让计算靠近数据;不要仅因 Worker 在全球运行就宣布后端全球化。

实验还要验证数据合规和 residency。请求可能在不同位置执行,bindings/数据产品有各自地域保证。敏感数据不能只依据延迟最优路由;记录处理位置、存储位置、跨境要求和平台合同。日志字段也可能跨区域进入集中系统,需要同样审查。

驱逐与并发演练

在 handler 全局放一个仅用于测试的实例标识和有界计数,观察不同请求可能复用或切换实例,但不把具体复用率写成契约。并发发送请求,在一个请求 await 时让另一个修改测试缓存,确认代码没有依赖原子请求级全局访问。随后部署新版本或等待实例变化,权威结果必须仍来自持久 binding,而不是内存。

关键后台任务演练分两组:只用 waitUntil 的 cache/log 可以故意失败而不影响业务正确性;订单/通知等任务必须先写 Queue/持久记录,模拟 consumer 重试仍只产生一次业务效果。给每个任务幂等键和状态,不能用“通常 waitUntil 会完成”代替交付保证。

Compatibility date 升级工作表

列出旧日期到新日期之间所有相关 flags/changelog,特别是 URL、Headers、streams、Node APIs、WebSocket、encoding、cache 和 error 行为。固定 Wrangler 与依赖,只改日期跑 contract;再固定日期升级 Wrangler,分离变量。staging 使用真实 bindings,canary 记录 deployment/date/outcome。回滚时确认新数据/cache 没有产生旧代码不可读格式。

Node compatibility package 单独检查 bundle 中 native/polyfill/stub。一个模块新日期下从 polyfill 变 native 可能改变边界行为和体积;import 成功仍要执行方法、失败、stream 与性能测试。长期依赖 stub 的包应替换或移回区域 runtime,不把 noop 当兼容。

上一篇:Bun · 返回专栏导读 · 下一篇:横向比较

在 GitHub 上编辑此页

本页目录