起因
某些场景"在边缘节点跑代码" 比"回源" 更优:
- 全球用户都需要的轻请求(API rate limit / auth check / A/B redirect)
- 静态站定制(个性化 header / cookie 处理)
- 流量塑形(特定 user 转到 staging)
- 短 latency API(< 50ms 全球)
Cloudflare Workers:V8 isolate 跑在 250+ 城市边缘节点。
冷启动 < 5ms(不是 Lambda 那种),按请求计费便宜。
hello world
// worker.js
export default {
async fetch(request, env, ctx) {
return new Response('Hello from the edge!');
},
};
部署:
npm install -g wrangler
wrangler init my-worker
cd my-worker
wrangler deploy
5 分钟得到 my-worker.user.workers.dev URL,全球 250+ 城市同时跑。
完整示例:geo redirect
export default {
async fetch(request) {
const country = request.cf.country; // CF 自动加 country header
const url = new URL(request.url);
if (country === 'CN' && url.hostname === 'example.com') {
return Response.redirect('https://cn.example.com' + url.pathname, 302);
}
// 其它转到 origin
return fetch(request);
},
};
中国用户自动重定向到 cn 子域,其它人正常回源。
A/B 测试
export default {
async fetch(request) {
const cookie = request.headers.get('cookie') || '';
let variant = parseCookie(cookie, 'variant');
if (!variant) {
variant = Math.random() < 0.5 ? 'A' : 'B';
}
const response = await fetch(
variant === 'B'
? request.url.replace('example.com', 'beta.example.com')
: request,
);
const newResponse = new Response(response.body, response);
newResponse.headers.set('set-cookie', `variant=${variant}; Path=/; Max-Age=86400`);
return newResponse;
},
};
50/50 分流,cookie 粘性。无需改后端。
鉴权 / API gateway
export default {
async fetch(request, env) {
const auth = request.headers.get('authorization');
if (!auth || !auth.startsWith('Bearer ')) {
return new Response('Unauthorized', { status: 401 });
}
const token = auth.slice(7);
// 验 JWT(用 Web Crypto API)
const valid = await verifyJWT(token, env.JWT_PUBLIC_KEY);
if (!valid) return new Response('Invalid token', { status: 401 });
// 加 user info 转到 origin
const newRequest = new Request(request);
newRequest.headers.set('x-user-id', valid.sub);
return fetch(newRequest);
},
};
边缘 JWT 验证 → 无效请求不打到 origin → 省 origin 带宽 / CPU。
KV / D1 / R2 存储
Workers 配套存储:
- KV:edge K/V,最终一致,读快写慢
- D1:SQLite at edge(每个 region 副本)
- R2:S3 兼容对象存储,无 egress 费
// 读 KV
const value = await env.MY_KV.get('user:42');
// D1 query
const result = await env.MY_DB.prepare(
'SELECT * FROM users WHERE id = ?').bind(42).first();
// R2 上传
await env.MY_BUCKET.put('file.bin', request.body);
Workers + KV / D1 / R2 全栈在边缘 → 静态站 + API + 数据全套。
Durable Objects
需要强一致 + stateful → DO:
export class Counter {
constructor(state, env) {
this.state = state;
}
async fetch(request) {
let count = (await this.state.storage.get('count')) || 0;
count++;
await this.state.storage.put('count', count);
return new Response(count.toString());
}
}
// Worker
export default {
async fetch(request, env) {
const id = env.COUNTER.idFromName('global');
const obj = env.COUNTER.get(id);
return obj.fetch(request);
},
};
DO 是全球唯一 instance(按 name),适合:counter / chat room /
collaborative state。WebSocket 跨用户的协作 app 杀手锏。
limits
- 单请求 CPU: 50ms (free) / 30s (paid)
- memory: 128 MB
- subrequest: 50 (free) / 1000 (paid)
- script size: 1 MB compressed (10 MB paid)
不像 Lambda 可以重计算几分钟。
Workers 是"轻量边缘 hook",不是通用 compute。
价格
- free tier: 100k req/day
- $5/月: 10M req
- $5 per additional 1M req
KV / R2 / D1 各自计费但都便宜。
对比 Lambda(cold start + GB-second + egress):高 QPS edge 场景 Workers
便宜 5-10x。
本地 dev
wrangler dev # 本地起 worker(用 V8 模拟)
wrangler dev --remote # 直接在 CF 边缘 dev
wrangler.toml:
name = "my-worker"
main = "src/index.js"
compatibility_date = "2024-05-25"
[vars]
ENVIRONMENT = "production"
[[kv_namespaces]]
binding = "MY_KV"
id = "..."
[[d1_databases]]
binding = "MY_DB"
database_name = "myapp"
database_id = "..."
TypeScript / Hono
// 用 Hono framework
import { Hono } from 'hono';
const app = new Hono();
app.get('/api/users/:id', async (c) => {
const id = c.req.param('id');
const user = await c.env.MY_DB.prepare(
'SELECT * FROM users WHERE id = ?').bind(id).first();
return c.json(user);
});
export default app;
Hono 是 Workers 上的 Express 替代,类型 + 路由友好。
跟 Lambda@Edge 对比
| CF Workers | AWS Lambda@Edge | |
|---|---|---|
| 启动 | < 5ms (V8 isolate) | 100-500ms (cold) |
| 语言 | JS / Wasm / Python | Node / Python |
| 限制 | 50ms CPU | 5s |
| 存储 | KV / D1 / R2 / DO | 需调外部 |
| 价格 | $0.50 / M req | $0.60 / M req(更贵) |
| Region | 250+ | CF 100+ |
Workers 现在边缘 compute 几乎事实标准。
AWS 也在推 Lambda@Edge 但慢。
真实 case:API rate limit
我们 API 想全球分布式 rate limit(不只是单 region)。
export default {
async fetch(request, env) {
const ip = request.headers.get('cf-connecting-ip');
const key = `rl:${ip}`;
const count = (await env.RL_KV.get(key)) || 0;
if (count >= 100) {
return new Response('Too Many Requests', { status: 429 });
}
await env.RL_KV.put(key, (parseInt(count) + 1).toString(), { expirationTtl: 60 });
return fetch(request);
},
};
每 IP 每分钟 100 req,跨 CF region 累计(KV 最终一致,5-10s 同步 →
某 region 用户可能 over limit 一点,可接受)。
强一致 → 用 DO + sliding window。
不适合的场景
- 长跑 CPU(视频转码 / ML inference):用 Workers AI / cloud function
- 大文件处理(> 100 MB body):直接 R2
- 复杂 stateful 流(DB transaction 多):传统 API server
踩过的坑
-
request body 不能多次读:
request.body是 stream,读一次。
需多用:const body = await request.text()先 buffer。 -
fetch subrequest 计数:每
fetch()算 1 subrequest,超 50
(free)报错。复杂 worker 谨慎。 -
KV 写 eventual consistency:刚写完读可能旧值。读 critical 用
D1 或 DO。 -
environment binding 没配:本地 dev 跑通,部署后
env.MY_KV
undefined → 看 wrangler.toml 是否 deploy。 -
package size 1MB:依赖大(如 lodash 全部 import)→ size 超。
tree-shake + 小依赖(zod / arktype 替代)。
登录后参与评论。