为什么选 Cloudflare Workers + D1
做 AI 应用最头疼的事情之一,就是控制 API 调用频率。用户一下子请求太多,你的 OpenAI 账单炸了;限制太严,用户体验又差。市面上有 Redis、有 Upstash,都能解决这个问题,但对独立开发者来说,多一个服务就多一份维护成本和账单。
Cloudflare Workers 配合 D1 数据库,可以零额外成本实现一套够用的速率限制方案。这篇文章给出完整的代码模板,拿去改改就能部署。
先说清楚适用场景。这套方案适合:
- 月调用量在 10 万次以下的 AI 应用
- 需要按用户或按 API Key 做速率限制
- 你已经在用 Cloudflare 做部署(Workers Pages 等)
不适合的场景也要说清楚:如果你的 QPS 超过几百,D1 的写入延迟会成为瓶颈,这时候应该上 Redis 或 Upstash。D1 本质是 SQLite,不是为高并发写入设计的。
根据 Cloudflare 2025 年开发者报告,Workers 平台上 AI 相关请求已超过 2000 亿次,边缘计算延迟控制在 80ms 以内。D1 作为 Workers 原生数据库,免费版提供 5GB 存储和 500 万次读/10 万次写每天,对中小项目完全够用。
第一步:创建 D1 数据库
# 创建数据库
wrangler d1 create ratelimit_db
# 创建表
wrangler d1 execute ratelimit_db --command "
CREATE TABLE IF NOT EXISTS rate_limits (
key TEXT NOT NULL,
window_start INTEGER NOT NULL,
count INTEGER DEFAULT 0,
PRIMARY KEY (key, window_start)
);"
第二步:wrangler.toml 配置
name = "ai-ratelimit-api"
main = "src/index.ts"
compatibility_date = "2024-12-01"
[[d1_databases]]
binding = "DB"
database_name = "ratelimit_db"
database_id = "你的数据库ID"
第三步:速率限制核心逻辑
// src/index.ts
export interface Env {
DB: D1Database;
OPENAI_API_KEY: string;
}
const WINDOW_SIZE = 60 * 1000; // 1分钟窗口
const MAX_REQUESTS = 10; // 每窗口最大请求数
async function checkRateLimit(db: D1Database, key: string) {
const now = Date.now();
const windowStart = Math.floor(now / WINDOW_SIZE) * WINDOW_SIZE;
const resetAt = windowStart + WINDOW_SIZE;
const result = await db.prepare(
"SELECT count FROM rate_limits WHERE key = ? AND window_start = ?"
).bind(key, windowStart).first();
const currentCount = (result?.count as number) || 0;
if (currentCount >= MAX_REQUESTS) {
return { allowed: false, remaining: 0, resetAt };
}
await db.prepare(
"INSERT INTO rate_limits (key, window_start, count) VALUES (?, ?, 1) " +
"ON CONFLICT(key, window_start) DO UPDATE SET count = count + 1"
).bind(key, windowStart).run();
return { allowed: true, remaining: MAX_REQUESTS - currentCount - 1, resetAt };
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const authHeader = request.headers.get("Authorization") || "";
const clientKey = authHeader.replace("Bearer ", "") ||
request.headers.get("CF-Connecting-IP") || "anonymous";
const { allowed, remaining, resetAt } = await checkRateLimit(env.DB, clientKey);
if (!allowed) {
return new Response(JSON.stringify({ error: "Rate limit exceeded" }), {
status: 429,
headers: {
"Content-Type": "application/json",
"X-RateLimit-Remaining": "0",
"X-RateLimit-Reset": String(resetAt),
"Retry-After": String(Math.ceil((resetAt - Date.now()) / 1000)),
},
});
}
if (request.url.includes("/chat")) {
const body = await request.json();
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + env.OPENAI_API_KEY,
},
body: JSON.stringify(body),
});
const data = await response.text();
return new Response(data, {
headers: {
"Content-Type": "application/json",
"X-RateLimit-Remaining": String(remaining),
},
});
}
return new Response("OK", {
headers: { "X-RateLimit-Remaining": String(remaining) },
});
},
};
第四步:部署
wrangler secret put OPENAI_API_KEY
wrangler deploy
部署完成后,你的 Workers 域名就成了一个带速率限制的 API 代理。客户端调用时会收到标准的速率限制头信息:X-RateLimit-Remaining(当前窗口剩余次数)、X-RateLimit-Reset(窗口重置时间戳)、Retry-After(超限时建议等待秒数)。
实际使用建议
清理过期数据。 D1 不会自动清理过期窗口的记录。建议加一个定时任务每小时清理:
async function cleanupOldRecords(db: D1Database) {
const cutoff = Date.now() - 3600 * 1000;
await db.prepare("DELETE FROM rate_limits WHERE window_start < ?").bind(cutoff).run();
}
在 wrangler.toml 中添加定时触发器:[triggers] / crons = ["0 * * * *"]
多级限流。 如果需要按用户和按 IP 同时限流,修改 key 的生成逻辑,将 API Key 和 IP 拼接作为唯一标识。
监控。 Cloudflare Workers 自带 Analytics,能看到请求量和错误率。429 响应时用 console.log 输出可记录到 Workers Logs。
成本对比
Cloudflare Workers 免费版每天 10 万次请求,D1 免费版每天 500 万次读和 10 万次写。一个速率限制每次请求需要 1 次读加 1 次写,所以免费版能支撑大约 10 万次 API 调用每天。付费版 5 美元/月包含 1000 万次请求。对比 Upstash Redis 免费版 1 万次命令/天。对于月调用量 10 万次以内的 AI 应用,这套方案基本零成本。
局限性
D1 的写入延迟在 5-15ms 左右,高并发场景下会拖慢整体响应。如果你的 QPS 超过 100,还是建议用 Redis。Cloudflare 自己也有 KV 和 Durable Objects,KV 适合全球分布但最终一致性,Durable Objects 适合强一致性但成本更高。
这套方案是固定窗口算法,不是滑动窗口。固定窗口的缺点是在窗口边界可能瞬间通过 2 倍的请求量。对大多数 AI 应用来说够用了,但如果你的限制很严格,可以考虑改用 Durable Objects 实现滑动窗口。
小结
Cloudflare Workers + D1 的速率限制方案,核心优势是零额外服务和零额外成本。代码模板可以直接拿去用,改一下 API 转发逻辑就能上线。适合月调用量 10 万次以内的 AI 应用,超出这个量级建议上 Redis 或 Upstash。
技术选型没有银弹,关键是匹配你的实际规模。别为了一个日活 50 的应用去搭 Redis 集群,那才是过度工程。
