Workers:运行时 API、Bindings 与执行模型

This article is extracted from the chat log with AI. Please identify it with caution.

说明:本文由 Codex 根据作者提供的主题、对话素材与 Cloudflare 官方文档辅助生成,属于 AI 生成/整理内容,非作者原创。请读者自行甄别并交叉验证。

值得单独写一篇。Workers 是 Cloudflare 开发平台的运行时底座;不先分清 handler、ctxenv binding 与运行时 API,后续接触 Queues、Durable Objects、Workflows、Dynamic Workers 时,很容易把“触发方式”“资源能力”和“部署管理 API”混在一起。

本文回答的是:代码运行在 Worker 内时,可以调用哪些 API、它们在什么边界内工作,以及资源如何通过 env 被授予给代码。它不展开各产品的全部方法表;Queues、Durable Objects、Workflows、Dynamic Workers 与 Sandbox 已各有专题,文末会给出对应入口。

先划清三层 API#

“Workers API”在讨论中常指三种不同的东西:

层次代码从哪里拿到解决什么问题例子
运行时 API全局对象或模块导入处理 HTTP、流、缓存、HTML、WebSocket、加密、出站 TCP 等fetch()Responsecaches.defaultHTMLRewriter
Binding APIhandler 的 env 参数访问被显式授予的 Cloudflare 资源env.DBenv.QUEUEenv.ROOMS
控制面 REST API带 API Token 的管理请求部署 Worker、管理版本、路由、账号资源Workers API、Account API

本文的重点是前两层。控制面 REST API 面向 CI、IaC、发布平台和运维后台;它不应被当成 Worker 请求路径中的常规数据访问方式。Worker 内需要访问 Cloudflare 平台资源时,优先使用 binding:它既是更直接的调用入口,也把可访问的能力限制在配置中。

flowchart LR
  T["HTTP / Cron / Queue / Email / Tail"] --> H["Module handlers"]
  H --> C["ctx:生命周期与异步任务"]
  H --> E["env:最小权限 Bindings"]
  H --> R["运行时 I/O:HTTP、流、缓存、HTML、WebSocket、TCP"]
  E --> P["KV · D1 · R2 · DO · Queues · Workflows · Services"]
  R --> O["Response / 事件确认 / 日志"]

Module Worker:最小执行模型#

新代码应使用 Module Worker:导出一个对象,按触发类型提供方法。旧式 addEventListener(…) Service Worker 写法仍可见于存量项目,但不适合作为新项目的默认选择。

// wrangler.jsonc
{
  "name": "workers-runtime-basics",
  "main": "src/index.ts",
  "compatibility_date": "2026-08-07",
  "vars": {
    "APP_NAME": "workers-runtime-basics"
  }
}
interface Env {
  APP_NAME: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/health") {
      return Response.json({
        ok: true,
        app: env.APP_NAME,
      });
    }

    return new Response("Not Found", { status: 404 });
  },
} satisfies ExportedHandler<Env>;

这里有三个稳定的入口:

  • request:本次 HTTP 请求;它的 body 是流,读取后不能自动再读一次。
  • env:配置、密钥与资源 binding 的容器。它不是任意全局环境变量。
  • ctx:本次调用的生命周期能力,例如在响应之后继续做短暂的非关键工作。

配置或 binding 改动后,运行 npx wrangler types 生成类型,能让 envRequestInit.cf 等接口跟随当前 Worker 配置校验。compatibility_date 也不是装饰字段:它决定 Worker 采用哪一组运行时行为;更新日期前应在测试环境验证。

Handler:什么事件会启动 Worker#

Handler典型签名触发源本文的使用建议
fetchfetch(request, env, ctx)HTTP 请求、Service Binding 的 HTTP 调用最常用入口;返回 Response
scheduledscheduled(controller, env, ctx)Cron Trigger定期扫描、汇总、触发后续任务;不要把它当作精确计时器。
queuequeue(batch, env, ctx)Queue consumer由批量消息驱动;确认、重试、DLQ 请见 /docs/Cloudflare/queues-api-and-practice/。
emailemail(message, env, ctx)Email Routing收件、校验、转发或入库;它需要邮件路由配置。
tailtail(events, env, ctx)Tail Worker 日志流独立处理日志/异常事件,避免在主请求中同步写入外部观测系统。
alarmDurable Object class 的 alarm() 方法某个 DO 的 Alarm不是普通 Worker 的顶层 handler;请见 /docs/Cloudflare/durable-objects/。

一个 Worker 可按需组合多个 handler。HTTP 路由通常留在 fetch;可靠的异步消费由 queue 接手;同一业务键下需要顺序协调、计时和状态,则交给一个 Durable Object。

ctx:响应、短暂后台工作与可靠任务的边界#

最常被误用的是 ctx.waitUntil()。它可以让一个 Promise 在 HTTP 响应发出后继续执行,但不是可靠队列:HTTP Worker 在响应完成或客户端断开后,所有 waitUntil() 共享的延长窗口最多约 30 秒。窗口结束、运行时异常或部署切换都可能让工作不能完成。

flowchart TD
  A["这项工作会影响当前响应吗?"] -->|会| B["直接 await;失败则明确返回错误"]
  A -->|不会| C["需要可靠执行、重试或超过短暂窗口吗?"]
  C -->|不需要| D["ctx.waitUntil(promise)"]
  C -->|需要可靠处理| E["投递 Queues"]
  E --> F["多步骤、等待事件或补偿?"]
  F -->|是| G["使用 Workflows"]
  F -->|否| H["Queue consumer 处理"]

适合 ctx.waitUntil() 的例子是写一条非关键访问日志、异步刷新一个公开缓存。订单扣款、webhook 投递、邮件发送、必须完成的数据写入,应使用 Queues;需要跨步骤重试、暂停、等待外部事件或补偿时,使用 /docs/Cloudflare/workflows-api-and-recovery/。

另外两个常见能力:

  • ctx.passThroughOnException():只适合“Worker 位于 origin 前、出现未处理异常时让请求回源”的 fail-open 代理。若请求 body 已被读取,运行时不能把它重新播放给 origin;它也无法绕过 CPU 或内存超限。
  • ctx.exportsctx.props:用于在同一 Worker 内生成受约束的 loopback binding / RPC 调用。前者需要 enable_ctx_exports compatibility flag,适合高级封装,不是普通模块间函数调用的替代品。

运行时 API 地图#

Workers 尽量采用 Web 标准,因此熟悉浏览器或 Node 的 Web API 后,大部分 I/O 可以直接迁移;差异集中在“边缘请求生命周期”和 Cloudflare 特有扩展。

类别常用 API适合做什么重要边界
HTTP 与 URLfetchRequestResponseHeadersURLFormData代理、API、边缘鉴权、请求改写Request/Response body 是单次消费的流;需复用时先 clone() 或设计流式管线。
流与编码ReadableStreamWritableStreamTransformStreamTextEncoder / TextDecoder大响应转发、流式生成、避免整包缓冲不要为了方便先把大 body 全部读进内存。
加密与标准能力crypto.subtleAbortSignalWebAssembly、计时 / Performance API签名校验、取消、Wasm 计算与测量Node 内建模块需要显式 Node.js compatibility 支持;优先先用 Web 标准接口。
Worker 扩展caches.defaultHTMLRewriterscheduler.wait()程序化缓存、流式 HTML 改写、可中止的延时都有 Workers 专属语义;不能照搬浏览器或 Node 的缓存/计时假设。
长连接WebSocketPairWebSocket单连接实时通信、协议升级需要多连接共享状态、协调或休眠恢复时,用 Durable Objects。
出站网络connect() from cloudflare:sockets用 stream 连接数据库代理、SMTP、专有 TCP 服务这是出站 TCP,不是用 Worker 监听入站 TCP 端口;每次调用中创建和关闭 socket。
Worker 间调用Service Binding、WorkerEntrypoint、RPC不经公网 URL 调用内部 WorkerHTTP 调用和 RPC 调用都要 await;否则被调 Worker 可能在完成前结束。

Cache API:程序化的、数据中心本地的缓存#

caches.default 提供 match()put()delete()。它与通过响应头驱动的 CDN 缓存不是同一个机制;写入的条目不会自动复制到所有数据中心。因此它适合热点公开对象的本地加速,不能被当作全局一致的数据层。

async function fetchPublicWithCache(
  request: Request,
  ctx: ExecutionContext,
): Promise<Response> {
  if (request.method !== "GET") {
    return fetch(request);
  }

  const cache = caches.default;
  const cached = await cache.match(request);
  if (cached) {
    return cached;
  }

  const response = await fetch(request);
  const canCache =
    response.ok &&
    !response.headers.has("Set-Cookie") &&
    !response.headers.get("Cache-Control")?.toLowerCase().includes("private");

  if (canCache) {
    ctx.waitUntil(
      cache.put(request, response.clone()).catch((error) => {
        console.error("cache write failed", error);
      }),
    );
  }

  return response;
}

上例只应服务于公开的 GET 响应。认证 header、用户 cookie、租户权限差异和私有数据都必须纳入 cache key 或直接绕开缓存;不要用这个模式缓存登录后的页面。

HTMLRewriter:边读边改 HTML#

HTMLRewriter 不需要把整个 HTML 读入内存。它以流式解析器的方式匹配选择器,在上游响应流经时改写标签或文档节点,适合注入安全属性、替换局部内容、边缘 A/B 标记等。

function addExternalLinkPolicy(response: Response): Response {
  return new HTMLRewriter()
    .on('a[target="_blank"]', {
      element(element) {
        element.setAttribute("rel", "noopener noreferrer");
      },
    })
    .transform(response);
}

处理文本时要注意回调可能多次收到同一个逻辑文本节点的 chunk;跨 chunk 的匹配需要自己维护状态。

WebSocket 与 TCP:两种不同的长连接#

WebSocket 是 HTTP 升级后的双向消息通道:

function acceptWebSocket(): Response {
  const [client, server] = Object.values(new WebSocketPair());

  server.accept();
  server.send("connected");

  return new Response(null, {
    status: 101,
    webSocket: client,
  });
}

这个模式适合单条连接的收发。聊天室、协同编辑、房间广播、限流桶等“多个连接必须由同一协调者管理”的场景,应将连接归属到 /docs/Cloudflare/durable-objects/。

TCP Socket 则用于 Worker 主动连出:

import { connect } from "cloudflare:sockets";

const socket = connect({ hostname: "db.example.internal", port: 5432 });
const writer = socket.writable.getWriter();
const reader = socket.readable.getReader();

它以 Web Streams 读写,并提供 openedclosedclose()startTls() 等接口。不要在 global scope 创建后跨请求复用 socket,也不要把它理解为“在 Worker 上开放一个入站 TCP 服务”。

env:Binding 是授予给代码的能力#

binding 可以理解为“配置注入的最小权限能力”:只有在 Wrangler 配置中声明并部署的资源,才会出现在 env。这种边界比在请求路径中携带管理 API Token 更清晰,也便于本地、预览和生产环境使用不同资源。

分类常见 binding何时使用
配置与安全环境变量、Secrets、Secrets Store、mTLS、Rate Limiting注入非敏感配置/密钥、管理凭据、服务间身份和边缘限流。
数据与状态KV、R2、D1、Durable Objects、Vectorize、Hyperdrive读取配置或对象、存储文件、关系数据、按业务键协调状态、向量检索、数据库连接优化。
异步与编排Queues、Workflows可靠消费与重试;跨步骤、可恢复的长流程。
服务组合与动态代码Service Bindings、Dynamic Worker Loaders、Dispatcher、Version Metadata内部微服务 RPC/HTTP、受控运行时加载、分发与版本识别。
AI、媒体与观测AI、Analytics Engine、Assets、Browser Run、Images、Media Transformations、Stream模型调用、指标写入、静态资源、浏览器自动化与媒体处理。

几个容易混淆的选择:

  • KV / R2 / D1 是数据服务;Durable Object 是带身份的状态协调者。需要针对单一业务键串行化时,优先考虑 DO。
  • Queue 是可靠异步消息通道;Workflow 是持久化步骤编排。不要用 waitUntil 冒充其中任何一个。
  • Service Binding 用于 Worker 到 Worker 的内部调用;它支持 env.CATALOG.fetch(request),也支持由 WorkerEntrypoint 暴露的 RPC 方法。内部调用不需要给目标服务公开 URL。
  • Dynamic Worker LoaderSandbox 面向受控动态执行和隔离环境;它们不是普通 import() 的替代品。详见 /docs/Cloudflare/dynamic-workers-api-and-security/ 与 /docs/Cloudflare/sandbox-sdk-api-and-practice/。

从需求反推 API#

需求首选能力不要误用成
路由、鉴权、改写请求、调用后端fetch + Request/Response在 Worker 中调用自己的公网 URL 形成无意义回环。
为公开内容做边缘局部缓存Cache API 或正确的 HTTP 缓存头caches.default 当全局数据库。
改写 HTML,不想整页读入内存HTMLRewriterawait response.text() 后再处理大型页面。
连接浏览器客户端WebSocketPair用普通 HTTP 轮询模拟双向实时协作。
协调一组 WebSocket、房间或锁Durable Objects把可变全局变量当共享状态。
在响应后做可丢失的小任务ctx.waitUntil()把关键任务放进 waitUntil
异步执行且需要重试、DLQQueuesfetch 内无限重试。
跨天等待、事件恢复、补偿Workflows用 Cron + 自建状态机拼接可靠流程。
Worker 内部调用Service Binding / RPC为内部服务暴露公网路由和长期 API Token。

实战检查清单#

  • 使用 Module Worker,并为每个环境固定且测试过的 compatibility_date
  • env 取得 binding;普通变量放 vars,密钥放 secret,不把 secret 写进仓库。
  • 把影响当前结果的 I/O await;只把可丢失、短暂的工作放入 ctx.waitUntil()
  • 把大 body 当 stream 处理;明确何处消费、何处 clone()
  • 缓存前先判断公开性、身份差异和数据中心本地性。
  • 多连接协调和按键串行化交给 Durable Objects;可靠异步交给 Queues;跨步骤恢复交给 Workflows。
  • 每次新增或修改 binding 后运行 npx wrangler types 与实际部署前的集成测试。

继续阅读#

本文共 5084 字,创建于 Aug 7, 2026

相关标签: Cloud, DevOps, TypeScript, ByAI