Cloudflare Cron Triggers 定时任务实战模式:从 API 数据同步到 Durable Objects 协调的九大场景指南
2026/9/12 2:36:11 网站建设 项目流程

Cloudflare Cron Triggers 定时任务实战模式:从 API 数据同步到 Durable Objects 协调的九大场景指南

【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills

本文围绕 Cloudflare Workers 的 Cron Triggers 定时任务机制,系统梳理 cloudflare-deploy Skill 参考文档 中给出的九大实战模式与本地/单元测试方法,并结合同目录下的 api.md、configuration.md、gotchas.md 与 README.md 进行源码级纵深解读。读完本文,你将掌握用scheduled()处理器编排数据同步、数据库清理、报表生成、健康检查、限速批处理、队列消费、可观测性埋点、分布式锁等定时任务的完整套路,并能在本地用/__scheduled端点与 Vitest 快速验证逻辑。

前置知识:scheduled() 处理器与模式总览

所有 Cron Trigger 模式的共同骨架都是scheduled(controller, env, ctx)入口,三个入参的分工如下(详见 api.md):

  • controller: ScheduledController:携带scheduledTime(Unix 毫秒时间戳)、cron(触发表达式)、type(恒为"scheduled")以及noRetry()方法,用于在预期失败时阻止自动重试;
  • env: Env:暴露全部绑定(KV、R2、D1、Secrets、Service Bindings、Queue、Durable Object 等);
  • ctx: ExecutionContext:提供ctx.waitUntil(promise)把异步任务(日志、清理、外部 API 调用)挂到执行尾部而不阻塞主流程。

在 SKILL.md 的决策树中,"Scheduled tasks (cron)" 被明确指向cron-triggers/参考文档。下表汇总了 patterns.md 中的全部模式及其核心载体:

模式核心业务关键绑定/依赖关键手法
API 数据同步拉取外部 API 写入 KVKV + 密钥ctx.waitUntil+ 带 TTL 缓存
数据库清理删除过期会话、回收空间D1参数化 SQL +VACUUM
报表生成聚合周报写入 R2 并通知D1 + R2 + 邮件/Webhook流式聚合 + 对象存储
健康检查探测多服务并告警KV + WebhookAbortSignal.timeout+Promise.all
限速批处理按批次消费队列数据KV(队列)+ 外部 APIPromise.allSettled+ 分批切片
队列集成消费 Cloudflare QueuesQueuereceive+ack
监控与可观测性记录执行元数据与告警KV + Webhook结构化日志 + 错误重抛
Durable Objects 协调防止多实例并发执行Durable Object分布式锁 +noRetry()
Python HandlerPython 版定时任务KV + D1WorkerEntrypoint子类

模式一:API 数据同步(API Data Sync)

将外部 API 的数据定时拉取并缓存到 KV,是最常见的 Cron Trigger 用法。scheduled()内先发起fetch请求,校验响应状态,再通过ctx.waitUntil把 KV 写入放到后台执行:

export default { async scheduled(controller, env, ctx) { const response = await fetch("https://api.example.com/data", {headers: { "Authorization": `Bearer ${env.API_KEY}` }}); if (!response.ok) throw new Error(`API error: ${response.status}`); ctx.waitUntil(env.MY_KV.put("cached_data", JSON.stringify(await response.json()), {expirationTtl: 3600})); }, };

要点解读:

  • 密钥绝不硬编码env.API_KEY来自 Secrets 绑定(通过wrangler secret put API_KEY注入),如 gotchas.md 所述,明文密钥是安全红线;
  • KV 带 TTL 自过期expirationTtl: 3600让缓存 1 小时后自动失效,配合定时刷新形成"拉取-缓存-过期-再拉取"的闭环;
  • 失败即抛错!response.ok时抛出异常,交由平台自动重试机制处理(详见后文"重试与失败处理")。

模式二:数据库清理(Database Cleanup)

针对 D1 数据库的过期数据清理任务,注意 SQL 使用datetime('now')以 SQLite 时间函数做条件过滤,并通过result.meta.changes获取受影响行数用于日志:

export default { async scheduled(controller, env, ctx) { const result = await env.DB.prepare(`DELETE FROM sessions WHERE expires_at < datetime('now')`).run(); console.log(`Deleted ${result.meta.changes} expired sessions`); ctx.waitUntil(env.DB.prepare("VACUUM").run()); }, };

两个细节值得注意:

  • 参数化查询原则:示例中的删除条件是常量;当条件来自外部输入(如动态阈值)时,应改用 Report Generation 模式 中的bind()占位符写法,避免 SQL 注入风险;
  • VACUUMwaitUntil:空间回收并非关键路径,用ctx.waitUntil挂起执行,避免阻塞主流程;console.log的行数输出可作为 Cron Events 的排查线索。

模式三:报表生成(Report Generation)

"查询聚合 → 落盘 R2 → 异步通知"三段式是报表类任务的经典流水线。本模式演示了 D1 的bind()参数化查询、reduce聚合与 R2 对象存储的组合使用:

export default { async scheduled(controller, env, ctx) { const startOfWeek = new Date(); startOfWeek.setDate(startOfWeek.getDate() - 7); const { results } = await env.DB.prepare(`SELECT date, revenue, orders FROM daily_stats WHERE date >= ? ORDER BY date`).bind(startOfWeek.toISOString()).all(); const report = {period: "weekly", totalRevenue: results.reduce((sum, d) => sum + d.revenue, 0), totalOrders: results.reduce((sum, d) => sum + d.orders, 0), dailyBreakdown: results}; const reportKey = `reports/weekly-${Date.now()}.json`; await env.REPORTS_BUCKET.put(reportKey, JSON.stringify(report)); ctx.waitUntil(env.SEND_EMAIL.fetch("https://example.com/send", {method: "POST", body: JSON.stringify({to: "team@example.com", subject: "Weekly Report", reportUrl: `https://reports.example.com/${reportKey}`})})); }, };
  • ?占位符 +bind():D1 的prepare(...).bind(...)是官方推荐的安全传参方式,startOfWeek.toISOString()保证了时间边界与 SQLite 存储格式一致;
  • 带时间戳的对象键reports/weekly-${Date.now()}.json保证每次生成的报表互不覆盖,天然支持历史留存与版本回溯;
  • Service Binding 异步通知env.SEND_EMAIL.fetch(...)演示了通过 Service Binding 调用另一个 Worker 发送邮件,放入waitUntil后即使通知失败也不影响报表落盘结果。

模式四:健康检查(Health Checks)

定时探测多个服务可用性,聚合结果写入 KV 供查询,并在存在故障时触发 Webhook 告警。核心技巧是AbortSignal.timeout(5000)给每个探测设置超时,避免单点挂起拖垮整个任务:

export default { async scheduled(controller, env, ctx) { const services = [{name: "API", url: "https://api.example.com/health"}, {name: "CDN", url: "https://cdn.example.com/health"}]; const checks = await Promise.all(services.map(async (service) => { const start = Date.now(); try { const response = await fetch(service.url, { signal: AbortSignal.timeout(5000) }); return {name: service.name, status: response.ok ? "up" : "down", responseTime: Date.now() - start}; } catch (error) { return {name: service.name, status: "down", responseTime: Date.now() - start, error: error.message}; } })); ctx.waitUntil(env.STATUS_KV.put("health_status", JSON.stringify(checks))); const failures = checks.filter(c => c.status === "down"); if (failures.length > 0) ctx.waitUntil(fetch(env.ALERT_WEBHOOK, {method: "POST", body: JSON.stringify({text: `${failures.length} service(s) down: ${failures.map(f => f.name).join(", ")}`})})); }, };
  • 超时兜底AbortSignal.timeout(5000)是 Web 标准 API,在 Workers 运行时可用;它把"服务无响应"这类典型故障转化为可捕获的异常,避免fetch无限期悬挂;
  • 全量并行 + 单点容错Promise.all并行探测所有服务,单个探测的try/catch保证一个服务异常不会导致整体失败,最终仍能给出完整的状态矩阵;
  • 状态持久化与告警分离:健康状态写入STATUS_KV(供前端/仪表盘读取),Webhook 告警仅在有故障时触发,避免噪声。

模式五:批量处理(限速,Batch Processing Rate-Limited)

当外部 API 有调用频率限制时,用 KV 充当轻量队列,每次调度只处理前 100 条,其余留给下一次触发:

export default { async scheduled(controller, env, ctx) { const queueData = await env.QUEUE_KV.get("pending_items", "json"); if (!queueData || queueData.length === 0) return; const batch = queueData.slice(0, 100); const results = await Promise.allSettled(batch.map(item => fetch("https://api.example.com/process", {method: "POST", headers: {"Authorization": `Bearer ${env.API_KEY}`, "Content-Type": "application/json"}, body: JSON.stringify(item)}))); console.log(`Processed ${results.filter(r => r.status === "fulfilled").length}/${batch.length} items`); ctx.waitUntil(env.QUEUE_KV.put("pending_items", JSON.stringify(queueData.slice(100)))); }, };
  • 切片消费slice(0, 100)取本批、slice(100)保留剩余,天然支持任意规模的积压任务分批消化;
  • Promise.allSettled而非Promise.all:单个请求失败不应中断整批处理,allSettled让所有请求都执行完,再通过r.status === "fulfilled"统计成功数;
  • 先记账后清理的原子性注意:示例在请求发出后立即用剩余队列覆盖 KV。若你需要"处理成功才出队",应结合 模式三 的落盘思路,或直接改用下文的 Cloudflare Queues(自带ack语义)。

模式六:队列集成(Queue Integration)

对于真正的高吞吐异步任务,应使用 Cloudflare Queues 而非 KV 模拟队列。本模式展示scheduled()定时批量消费队列消息并逐条ack

export default { async scheduled(controller, env, ctx) { const batch = await env.MY_QUEUE.receive({ batchSize: 100 }); const results = await Promise.allSettled(batch.messages.map(async (msg) => { await processMessage(msg.body, env); await msg.ack(); })); console.log(`Processed ${results.filter(r => r.status === "fulfilled").length}/${batch.messages.length}`); }, };
  • receive({ batchSize: 100 }):按需拉取一批消息,batchSize与 CPU 限额配合可控制单次执行成本;
  • msg.ack()显式确认:只有处理成功的消息才确认,失败消息会保留在队列中,由平台重投,与 Cron Trigger 的"at-least-once"语义互补;
  • 与限速批处理的取舍:KV 方案适合"低频、数据量可控、外部 API 限速"场景;Queues 方案适合"高频、需要重试投递、需要生产消费分离"场景。二者在 README.md 中被分别对应到"存储"与"异步处理"两条能力路径。

模式七:监控与可观测性(Monitoring & Observability)

为定时任务自身建立监控闭环:记录 cron 元数据、成功/失败的结构化日志、执行耗时,失败时告警并重新抛出异常以触发平台自动重试

export default { async scheduled(controller, env, ctx) { const startTime = Date.now(); const meta = { cron: controller.cron, scheduledTime: controller.scheduledTime }; console.log("[START]", meta); try { const result = await performTask(env); console.log("[SUCCESS]", { ...meta, duration: Date.now() - startTime, count: result.count }); ctx.waitUntil(env.METRICS.put(`cron:${controller.scheduledTime}`, JSON.stringify({ ...meta, status: "success" }), { expirationTtl: 2592000 })); } catch (error) { console.error("[ERROR]", { ...meta, duration: Date.now() - startTime, error: error.message }); ctx.waitUntil(fetch(env.ALERT_WEBHOOK, { method: "POST", body: JSON.stringify({ text: `Cron failed: ${controller.cron}`, error: error.message }) })); throw error; } }, };
  • 观察日志npx wrangler tail实时流式查看,或 Dashboard → Workers & Pages → Worker → Logs 查看历史日志;
  • [START]/[SUCCESS]/[ERROR]结构化前缀:便于日志检索与告警规则匹配,meta携带的cron表达式与scheduledTime是排查"哪条计划、哪个时刻"的关键维度;
  • throw error保留重试权:与 api.md 的错误处理最佳实践一致——失败任务的执行会被自动重试(通常在延迟数分钟后),除非显式调用controller.noRetry()
  • 指标落 KVexpirationTtl: 2592000(30 天)让近期执行记录可回溯,同时避免无限堆积。

模式八:Durable Objects 协调(Durable Objects Coordination)

由于 Cron Trigger 采用at-least-once 投递且可能产生重复执行,当任务对并发敏感(如只允许一个实例执行)时,可用 Durable Object 实现分布式锁:

export default { async scheduled(controller, env, ctx) { const stub = env.COORDINATOR.get(env.COORDINATOR.idFromName("cron-lock")); const acquired = await stub.tryAcquireLock(controller.scheduledTime); if (!acquired) { controller.noRetry(); return; } try { await performTask(env); } finally { await stub.releaseLock(); } }, };
  • 固定 ID 寻址idFromName("cron-lock")保证所有调度实例都落到同一个 Durable Object 实例,锁的"唯一性"由此保证;
  • 拿不到锁就不重试controller.noRetry()告知平台"本次跳过是预期的",避免无意义的自动重试;这与 api.md 中"检测到重复执行时不重试"的建议完全对应;
  • finally释放锁:无论任务成功与否都释放锁,防止死锁;tryAcquireLock/releaseLock由你的 Durable Object 类实现(本模式未展示其内部实现,可参考仓库中的 durable-objects 参考文档)。

模式九:Python Handler(Python 定时处理器)

Cloudflare Workers 同样支持 Python。与 TypeScript 的export default { scheduled }不同,Python 需继承WorkerEntrypoint并实现scheduled方法:

from workers import WorkerEntrypoint class Default(WorkerEntrypoint): async def scheduled(self, controller, env, ctx): data = await env.MY_KV.get("key") ctx.waitUntil(env.DB.execute("DELETE FROM logs WHERE created_at < datetime('now', '-7 days')"))
  • 类即入口class Default(WorkerEntrypoint)是 Python Worker 的默认入口约定,与 TypeScript 的 default export 语义对齐;
  • 绑定接口一致env.MY_KVenv.DB的用法与 TypeScript 版本保持同构,ctx.waitUntil语义不变;
  • 适合场景:数据清洗、ETL 等偏脚本化的定时逻辑,可直接复用 Python 生态的既有代码。

测试模式:本地触发与单元测试(Testing Patterns)

本地开发:/__scheduled 端点

启动开发服务器后,Cloudflare 暴露http://localhost:8787/__scheduled端点用于手动触发scheduled()

# Start dev server npx wrangler dev # Test specific cron curl "http://localhost:8787/__scheduled?cron=*/5+*+*+*+*" # Test with specific time curl "http://localhost:8787/__scheduled?cron=0+2+*+*+*&scheduledTime=1704067200000"

查询参数说明(详见 api.md):

  • cron:必填,URL 编码后的 cron 表达式,空格用+代替*/5 * * * *的写法会导致 404/不触发);
  • scheduledTime:可选,Unix 毫秒时间戳,缺省为当前时间,用于模拟"过去/未来的某个调度时刻"(如验证 idempotency)。

⚠️ 安全提醒:/__scheduled端点在生产环境同样可用且可被任何人触发。上线前必须在fetch()中拦截该路径(返回 404),或实现认证校验,详见 gotchas.md 中的ENVIRONMENT === "production"拦截示例。

单元测试:Vitest + cloudflare:test

使用 Vitest 与cloudflare:test环境,构造假的controllerctx直接调用worker.scheduled()

// test/scheduled.test.ts import { describe, it, expect, vi } from "vitest"; import { env } from "cloudflare:test"; import worker from "../src/index"; describe("Scheduled Handler", () => { it("executes cron", async () => { const controller = { scheduledTime: Date.now(), cron: "*/5 * * * *", type: "scheduled" as const, noRetry: vi.fn() }; const ctx = { waitUntil: vi.fn(), passThroughOnException: vi.fn() }; await worker.scheduled(controller, env, ctx); expect(await env.MY_KV.get("last_run")).toBeDefined(); }); it("calls noRetry on duplicate", async () => { const controller = { scheduledTime: 1704067200000, cron: "0 2 * * *", type: "scheduled" as const, noRetry: vi.fn() }; await env.EXECUTIONS.put("0 2 * * *-1704067200000", "1"); await worker.scheduled(controller, env, { waitUntil: vi.fn(), passThroughOnException: vi.fn() }); expect(controller.noRetry).toHaveBeenCalled(); }); });

测试设计要点(参考 gotchas.md):

  • mock 三要素ScheduledController(含noRetry)、ExecutionContext(含waitUntil)、以及所有 KV/D1/Queue 绑定;
  • 逐条 cron 分别测试:多计划 Worker 用switch (controller.cron)路由时,每个分支都应有一个用例;
  • 验证noRetry()调用时机:第二个用例先向 KV 预置重复执行标记,再断言noRetry被调用,直接验证幂等逻辑;
  • 生产建议:上线先用长间隔(如*/30 * * * *)观察 24 小时 Cron Events,确认无误后再缩短间隔。

常见陷阱与幂等性补充(Gotchas)

本仓库的 gotchas.md 对模式落地中的高频问题给出了针对性解法,与上文模式直接互补:

  • 时区陷阱:所有 cron 均为UTC 执行,无本地时区支持。换算公式utcHour = (localHour - utcOffset + 24) % 24:9am PST(UTC-8)→0 17 * * *;6pm JST(UTC+9)→0 9 * * *。夏令时需手工调整;
  • 重复执行(幂等性):at-least-once 投递可能产生重复副作用(重复扣款、重复发信)。用 KV 记录执行 ID(${cron}-${scheduledTime})并设置 24h TTL 去重;拿不到锁 / 检测到重复时调用noRetry()跳过;
  • waitUntil 任务静默失败ctx.waitUntil(riskyOperation())的 rejected promise 会被静默吞掉,务必.catch()显式处理;首次waitUntil失败会被记录进 Cron Events;
  • 计划限额:Free 套餐每 Worker 3 个触发器、10ms CPU;Paid 无限触发器、50ms CPU;最短间隔 1 分钟,精度约 ±1 分钟,配置见 README.md;
  • 部署传播延迟:cron 变更最多需 15 分钟全局生效,验证时务必留足等待时间。

进一步阅读

  • cron-triggers README - 概览、cron 语法与快速开始
  • cron-triggers api.md - ScheduledController、noRetry()、waitUntil、多计划路由
  • cron-triggers configuration.md - wrangler.jsonc 配置、环境差异化计划、Green Compute
  • cron-triggers gotchas.md - 时区、幂等性、安全与故障排查
  • workflows 参考文档 - 长时多步定时任务的替代方案
  • cloudflare-deploy SKILL.md - 产品选型决策树与部署前置检查

【免费下载链接】skillsSkills Catalog for Codex项目地址: https://gitcode.com/GitHub_Trending/skills4/skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询