Cloudflare Workers 是一个基于 V8 Isolate 的 Serverless 边缘计算平台:你的 JavaScript/TypeScript 代码被打包后分发到 Cloudflare 全球 300+ 个数据中心,在用户请求落地的最近节点上直接执行。与传统 Serverless(如 AWS Lambda)最大的区别在于:Worker 不为每个请求拉起容器或虚拟机,而是复用同一个 Node 进程中的 V8 隔离实例,因此冷启动时间趋近于零(通常 <1ms),而 Lambda 的冷启动动辄几百毫秒甚至数秒。
如果你想先建立对 Cloudflare 整个生态的全局认识,建议先读Cloudflare 详解:从 CDN 到全球边缘计算平台,再回到本文动手实操。
运行时模型:为什么能做到零冷启动
理解 Workers 的运行模型,是写好它的前提。
V8 Isolate vs 容器
Lambda 等 FaaS 平台为每个函数实例启动一个微型容器(Firecracker microVM),容器有完整的操作系统、文件系统和进程模型,代价是冷启动需要初始化整个运行环境。Workers 走了另一条路:Cloudflare 在自己的边缘节点上运行一个长期存活的 workerd 进程(基于 V8 引擎),你的代码只是这个进程里一个相互隔离的 Isolate——共享同一个进程,但拥有独立的堆内存和全局作用域。
这带来几个直接的工程意义:
- 冷启动 <1ms:没有容器拉起、没有运行时初始化,代码已经在内存里。
- 同请求并发能力极强:一个边缘节点可以轻松承载数千个并发 Worker 请求。
- 计费按 CPU 时间:你只为自己代码实际占用的 CPU 时间付费,等待网络 I/O(如调用外部 API)的时间不计费。
必须知道的限制
- CPU 时间:免费计划单次请求 CPU 上限 10ms,付费计划默认 50ms(可配置到 5 分钟)。注意这是 CPU 时间,不是墙钟时间——你
await fetch(...)等外部 API 的那 2 秒不计入。 - 内存:单次请求隔离内存上限 128MB。
- 没有本地文件系统:不能写临时文件,不能用
fs模块。文件类需求用 R2 或外部对象存储。 - 不支持任意 Node.js API:Workers 实现了标准的 Web API(
fetch、Request、Response、crypto、TextEncoder等),并正在持续补全 Node.js 兼容层(nodejs_compat),但不是完整 Node 运行时。 - 响应大小:免费计划脚本压缩后不超过 1MB,付费 10MB。
快速上手:从安装到部署
安装 wrangler 并登录
wrangler 是 Cloudflare 官方的 Workers CLI:
npm install -g wrangler
wrangler login
wrangler login 会打开浏览器完成 OAuth 授权。也可以用 wrangler login --browser false 在 CI 环境配合 API Token 使用。
创建第一个 Worker
npm create cloudflare@latest -- my-first-worker
cd my-first-worker
脚手架会问几个交互式问题:选择 “Hello World” 类型、TypeScript 或 JavaScript、是否部署。生成的核心文件 src/index.js 内容大致如下,注意这是现代 modules 格式(2022 年后官方推荐,也是唯一推荐格式):
export default {
async fetch(request, env, ctx) {
return new Response('Hello from the edge!', {
headers: { 'content-type': 'text/plain;charset=UTF-8' },
});
},
};
三个参数的含义:
request:标准 Fetch API 的Request对象。env:绑定的环境变量、KV 命名空间、密钥等全部挂在它上面。ctx:执行上下文,提供waitUntil()(请求返回后继续跑后台任务)和passThroughOnException()。
本地开发与部署
# 本地开发,默认 http://localhost:8787
wrangler dev
# 发布到 *.workers.dev 子域名
wrangler deploy
wrangler dev 支持热重载,改代码即生效,且可以在本地模拟 KV、D1、R2 等绑定。wrangler deploy 几秒内就会把代码同步到全球边缘网络——这就是边缘部署的快感:没有镜像构建,没有滚动更新,一次发布全球生效。
路由实战:写一个真实的 JSON API
Worker 没有内置路由框架,自己基于 URL 对象写路由其实很直观,也是官方文档的标准做法:
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// 统一处理 CORS 预检
if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
// 基于路径与方法的分发
if (url.pathname === '/api/health' && request.method === 'GET') {
return json({ status: 'ok', colo: request.cf?.colo });
}
if (url.pathname.startsWith('/api/users/')) {
const id = url.pathname.split('/').pop();
if (request.method === 'GET') return json(await getUser(env, id));
if (request.method === 'POST') return json(await updateUser(env, id, request), { status: 201 });
return json({ error: 'Method Not Allowed' }, { status: 405 });
}
return json({ error: 'Not Found' }, { status: 404 });
},
};
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
function json(data, init = {}) {
return new Response(JSON.stringify(data), {
...init,
headers: { 'content-type': 'application/json;charset=UTF-8', ...corsHeaders, ...init.headers },
});
}
async function getUser(env, id) {
return { id, name: 'demo' };
}
async function updateUser(env, id, request) {
const body = await request.json();
return { id, ...body };
}
几个要点:
request.cf包含 Cloudflare 注入的请求元数据:colo(处理请求的数据中心三字码)、country、asn等,做地理路由或审计时非常好用。- 处理请求体用标准方法:
await request.json()、request.formData()、request.text()。 - CORS 记得覆盖
OPTIONS预检,否则浏览器端 fetch 会直接失败。
当路由复杂起来,可以引入 Hono 这类轻量框架,它也是为 Workers 量身优化的,详见Better Auth 完整接入指南:基于 React、Hono、Cloudflare Workers 与 D1。
KV 存储实战:读写你的第一个键值
Workers KV 是一个全球分布的最终一致性键值存储,适合读多写少、对延迟敏感的数据:配置、特性开关、会话、缓存。
创建命名空间并绑定
wrangler kv namespace create "SESSIONS"
# 输出:🌀 Creating namespace "SESSIONS" ✨ Success!
# Add the following to your wrangler.toml:
# { binding = "SESSIONS", id = "abc123..." }
把返回的配置加入 wrangler.toml:
name = "my-first-worker"
main = "src/index.js"
compatibility_date = "2026-07-01"
[[kv_namespaces]]
binding = "SESSIONS"
id = "abc123..."
binding 名字决定了代码里的访问方式:env.SESSIONS。
读写删与 TTL
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname === '/session') {
const sessionId = crypto.randomUUID();
// 写入,TTL 3600 秒后自动过期
await env.SESSIONS.put(
`session:${sessionId}`,
JSON.stringify({ userId: 42, createdAt: Date.now() }),
{ expirationTtl: 3600 }
);
// 读取:value 不存在时返回 null
const raw = await env.SESSIONS.get(`session:${sessionId}`);
const session = raw ? JSON.parse(raw) : null;
// 删除
// await env.SESSIONS.delete(`session:${sessionId}`);
return new Response(JSON.stringify({ sessionId, session }), {
headers: { 'content-type': 'application/json' },
});
}
return new Response('Not Found', { status: 404 });
},
};
API 要点:
get(key)/get(key, 'json')/get(key, 'text')/get(key, 'arrayBuffer'),默认返回字符串。put(key, value, { expirationTtl }),TTL 最短 60 秒;也可以用expiration指定绝对 Unix 时间戳。delete(key)、list({ prefix, limit, cursor })支持分页列出。
最终一致性的坑
KV 是最终一致的:写入后,数据在全球边缘节点的传播通常需要几十秒。同一个节点读会立即命中新值,但换一个节点可能读到旧值。推论:
- 适合:读远多于写的数据——配置、A/B 实验开关、短 TTL 的缓存、非强一致的会话。
- 不适合:库存扣减、分布式锁、排行榜这类写后即读的强一致场景,那应该用 D1 或 Durable Objects。
另外 KV 写操作(put/delete/list)有速率限制(同一 key 每秒 1 次写),热点写入要绕开。
Cache API 实战:给源站减压
除了 KV,Worker 还能直接操控边缘缓存 caches.default,把任意 Response 缓存到当前数据中心:
export default {
async fetch(request, env, ctx) {
const cache = caches.default;
// 自定义 cache key:把设备类型拼进 URL,实现按 UA 分片缓存
const url = new URL(request.url);
const device = request.headers.get('user-agent')?.includes('Mobile') ? 'mobile' : 'desktop';
const cacheKey = new Request(`${url.origin}${url.pathname}?device=${device}`, request);
let response = await cache.match(cacheKey);
if (response) {
return new Response(response.body, {
...Object.fromEntries(response.headers),
headers: { ...Object.fromEntries(response.headers), 'X-Cache': 'HIT' },
});
}
// 回源
response = await fetch('https://origin.example.com' + url.pathname);
// 只缓存成功的响应,并强制边缘缓存 60 秒
if (response.ok) {
const cached = new Response(response.body, response);
cached.headers.set('Cache-Control', 'public, max-age=60');
cached.headers.set('X-Cache', 'MISS');
ctx.waitUntil(cache.put(cacheKey, cached.clone()));
return cached;
}
return response;
},
};
关键点:
caches.default是每个数据中心独立的 Cache 实例,免费计划即可用。ctx.waitUntil()让cache.put在响应返回后异步完成,不阻塞用户。- 自定义 cache key 是核心能力:默认以整个请求(含完整 URL)为 key,你可以拼接查询参数或头部特征,实现细粒度的缓存分片,比如按语言、按设备。
- 缓存的响应必须设置
Cache-Control头,否则cache.put会静默失败。
这套机制常用于在 Worker 里给传统源站套一层边缘缓存:动态接口短时间缓存 5-60 秒,就能把突发流量挡在边缘,源站压力骤降。
环境变量与密钥
wrangler.toml 里声明普通变量:
[vars]
ENVIRONMENT = "production"
API_BASE = "https://api.example.com"
敏感值(API Key、数据库密码)绝不能写进 toml,用 secret:
wrangler secret put STRIPE_SECRET_KEY
# 交互式输入,加密存储
代码里统一从 env 读取:
const env_name = env.ENVIRONMENT; // vars
const stripeKey = env.STRIPE_SECRET_KEY; // secret,读取方式完全一样
多环境用 toml 的 [env.<name>] 段:
name = "my-first-worker"
main = "src/index.js"
compatibility_date = "2026-07-01"
[vars]
ENVIRONMENT = "default"
[env.staging]
name = "my-first-worker-staging"
[env.staging.vars]
ENVIRONMENT = "staging"
[env.production]
name = "my-first-worker-prod"
[env.production.vars]
ENVIRONMENT = "production"
部署时 wrangler deploy --env staging / --env production,secret 也需要按环境分别 wrangler secret put --env production ...。
生产建议
实时日志与排错
wrangler tail
wrangler tail 会把边缘节点上你 Worker 的 console.log 和未捕获异常实时流式输出到终端,是排障的第一工具。也可以在 Cloudflare Dashboard 的 Workers 面板看请求量、错误率、CPU 时间中位数等指标。
错误处理
- 全局兜底:在
fetch最外层包一层 try/catch,异常时返回统一格式的 500 响应,避免泄露堆栈。 - 兜底响应:
ctx.passThroughOnException()可以让 Worker 异常时请求直接透传到源站,适合 Worker 做缓存层/中间层的场景。 - 超时意识:外部 API 调用加
AbortSignal.timeout(5000),防止上游拖死自己。
与 Pages Functions 的关系
Pages Functions 本质上是托管在 Pages 项目下的 Workers:你在 functions/ 目录写的中间件、API 路由,最终都以 Worker 形式部署。区别在于 Pages Functions 没有独立的 wrangler.toml,绑定走 Dashboard 或 Pages 项目的 wrangler 配置,适合"前端站点 + 少量 API"的组合。纯 API 或复杂后端,直接用独立 Worker 更灵活。关于静态站托管可参考Cloudflare Pages 完全指南。
进阶路线
学完本文的 KV 与 Cache 后,可以按需深入:D1(边缘 SQLite 关系数据库)、Durable Objects(强一致协调器,做 WebSocket、房间、限流)、R2(对象存储)、Queues(异步任务)。想直接看一个把这些组件拼起来的全栈案例,可以读React + Hono + Better Auth 全栈技术方案:构建 Cloudflare 原生 SaaS 应用。
常见问题(FAQ)
Cloudflare Workers 免费额度是多少?
免费计划每天 100,000 次请求,单次请求 CPU 时间上限 10ms,KV 每天 100,000 次读、1,000 次写、1GB 存储。对个人项目和小型 API 完全够用。付费计划(Workers Paid)$5/月起,包含 1000 万次请求,超出按量计费。
Workers 支持 Node.js API 吗?
部分支持。在 wrangler.toml 中加 compatibility_flags = ["nodejs_compat"] 后,可以使用 Buffer、process.env、crypto、stream 等常用 Node 模块,兼容范围在持续扩大。但不是完整 Node 运行时,fs、原生 TCP 套接字等仍不可用。写代码优先用标准 Web API(fetch、Response、crypto.subtle),可移植性最好。
Workers 能直接连数据库吗?
可以,但有讲究。Workers 支持出站的 TCP/HTTP 连接,最佳实践是:用 Cloudflare 自家的 D1(SQLite)或 Hyperdrive(加速连接外部 Postgres/MySQL 的连接池代理),或者走数据库厂商提供的 HTTP API(如 Neon Serverless、Supabase REST)。直接维持长 TCP 连接的传统驱动在边缘环境下容易遇到连接管理问题。
Worker 和 Pages Functions 怎么选?
前端站点为主、API 只有几个端点:选 Pages Functions,部署和路由跟站点一体。纯后端 API、需要独立版本管理和多环境、或要用到 Durable Objects/Queues/Cron:选独立 Worker。两者底层是同一个运行时,能力一致,差别在工程组织方式。
CPU 时间超限了怎么办?
报错 Worker exceeded CPU time limit 说明代码在单次请求里做了太多计算。排查方向:把 JSON 大对象解析改流式、避免正则灾难回溯、把非必要计算挪到 ctx.waitUntil() 里的后台任务,或拆到 Queues 异步处理。付费计划还可以在设置里把 CPU 上限从 50ms 提到最高 5 分钟(CPU 计费也相应增加)。
相关阅读
- Cloudflare 详解:从 CDN 到全球边缘计算平台
- Cloudflare Pages 完全指南
- Cloudflare Workers AI 与 AI Gateway 入门:边缘推理与统一代理
- Better Auth 完整接入指南:基于 React、Hono、Cloudflare Workers 与 D1
- React + Hono + Better Auth 全栈技术方案:构建 Cloudflare 原生 SaaS 应用
- Cloudflare 专题导航
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。