资讯详情

autoskills 实战指南:Cloudflare Cron Triggers 常用模式完整解析(含源码级测试验证)

📅 2026/10/9 12:34:31 | 华诺云谱 👁 阅读
autoskills 实战指南:Cloudflare Cron Triggers 常用模式完整解析(含源码级测试验证)
【免费下载链接】autoskillsOne command. Your entire AI skill stack. Installed.项目地址https://gitcode.com/gh_mirrors/au/autoskills点击查看免费下载本篇技术指南以 autoskills 仓库内 cloudflare-deploy 技能包 中 Cron Triggers Patterns 文档 为骨架系统梳理 Cloudflare Workers 定时任务Cron Triggers的九大实战模式API 数据同步、数据库清理、报表生成、健康检查、限速批量处理、队列集成、监控可观测性、Durable Objects 协调与 Python Handler并完整覆盖本地测试与单元测试方案。读完本文你将能够基于scheduled处理器与ctx.waitUntil构建可上生产、可测试、可观测的定时任务体系并规避 at-least-once 投递带来的重复执行问题。一、前置认知Cron Triggers 的运行模型在展开模式之前先明确 Cloudflare Cron Triggers 的底层运行约束这些约束直接决定下面每个模式的设计取舍详见 cron-triggers/README.mdUTC-only 调度所有 cron 表达式一律按 UTC 执行无本地时区支持换算公式为utcHour (localHour - utcOffset 24) % 245 字段 cron 语法支持 Quartz 扩展字符L最后、W工作日、#第 N 个最小间隔 1 分钟精度约 ±1 分钟全局传播延迟改动最多需要 15 分钟才能在全球网络生效at-least-once 投递极少情况下可能出现重复执行因此幂等性是所有模式的第一原则CPU 配额Free 计划每 Worker 3 个触发器、10ms CPUPaid 计划触发器不限、50ms CPU超时任务需借助ctx.waitUntil()或 Workflows。patterns.md中的每个示例都遵循统一的处理器签名export default { async scheduled(controller, env, ctx) }。其中controller.scheduledTime为 Unix 毫秒时间戳、controller.cron为触发表达式、ctx.waitUntil(promise)用于把后台异步任务挂接到执行上下文让 Worker 在处理器返回后继续完成这些任务详见 cron-triggers/api.md。二、API 数据同步API Data Sync定时从外部 API 拉取数据并缓存到 KV是 Cron Triggers 最基础也最常见的用法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})); }, };设计要点密钥走 Bindingenv.API_KEY必须是 Worker 的 Secret 绑定绝不可硬编码进源码cron-triggers/gotchas.md 的安全章节明确要求用env.API_KEY管理密钥显式失败传播!response.ok时抛出异常让运行时自动重试前提是未调用controller.noRetry()KV 缓存过期expirationTtl: 3600表示缓存数据 1 小时后过期适合数据时效性可容忍、读取性能优先的场景写入本身是后台任务通过ctx.waitUntil异步完成避免占用主路径 CPU 配额。三、数据库清理Database Cleanup定时清理过期数据将维护任务从请求热路径剥离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()); }, };要点说明使用 D1 数据库的prepare().run()执行原生 SQLite 删除语句datetime(now)直接以 SQLite 内置函数比较过期时间无需在 JS 侧换算时间戳result.meta.changes返回实际删除的行数是衡量清理任务效果的直观指标应写入日志便于后续观测VACUUM回收数据库文件空间属于典型的耗时后台操作必须放在ctx.waitUntil中执行对数据量极大的清理建议拆分为分批删除见下文限速批量处理模式避免单次执行超过 CPU 配额。若删除逻辑过于复杂或重可改用 Workflows 承载长时任务见 cron-triggers/api.md 的 Workflow Integration 章节。四、报表生成Report Generation把数据库聚合结果写成 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: teamexample.com, subject: Weekly Report, reportUrl: https://reports.example.com/${reportKey}})})); }, };要点说明参数化查询WHERE date ?配合.bind(startOfWeek.toISOString())防止 SQL 注入是 D1 查询的标准写法纯函数式聚合用reduce汇总周收入与订单数dailyBreakdown保留原始明细形成一份自包含的 JSON 报表R2 对象键含时间戳reports/weekly-${Date.now()}.json天然避免同名覆盖便于保留历史报表通知异步化SEND_EMAIL服务绑定Service Binding的fetch调用放入ctx.waitUntil即使邮件服务慢也不会拖累主流程。五、健康检查Health Checks对多个上游服务做并行探测状态落 KV 并在异常时告警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)确保单个服务无响应时 5 秒内返回避免检查任务整体挂死——这是 gotchas.md 中Execution Failures一节推荐的AbortController超时实践try/catch 全捕获fetch 抛出的网络异常被转换为{status: down, error}结构保证任何服务失败都不会中断整体检查状态可查health_status写入 KV 后任意请求路径都能通过 KV 读取最近的健康快照告警去重只有出现失败时才触发 Webhook POST且告警本身异步发送ctx.waitUntil。六、限速批量处理Batch ProcessingRate-Limited把待处理队列切成小批次逐轮消费既控制外部 API 压力又避免单次 CPU 超限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)))); }, };要点说明KV 即队列pending_items以 JSON 数组形式保存在 KV 中每次取出前 100 条slice(0, 100)并发 容错Promise.allSettled让单个请求失败不影响其余请求处理完统计fulfilled数量写日志进度推进处理完成后把剩余数据slice(100)写回 KV配合 cron 周期如每 5 分钟即可实现每次处理一批、多轮耗尽的节流效果失败重试语义由于 at-least-once 投递scheduled失败重跑时会重新处理当前批次因此处理函数本身应尽量幂等或结合下文监控与幂等小节兜底。七、队列集成Queue Integration若已使用 Cloudflare Queues 做消息缓冲可直接在 cron 中批量消费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 })一次拉取至多 100 条消息把生产者入队、消费者定时拉取解耦显式确认每条消息处理成功后调用msg.ack()只有确认的消息才会从队列移除未确认消息将按队列重试策略重新投递与队列原生触发对比Cron Triggers 适合定时批量拉取场景若需要实时即推即处理应直接使用 Queues 的 consumer 绑定二者互为补充参见 SKILL.md 中 Need to store data 决策树对 Queues 的定位。八、监控与可观测性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; } }, };要点说明三段式日志[START]/[SUCCESS]/[ERROR]配合controller.cron与controller.scheduledTime让每条日志可精确回溯到哪次调度、哪个表达式耗时度量Date.now() - startTime记录任务总耗时用于判断是否逼近 CPU 配额Free 10ms / Paid 50ms指标持久化成功记录写入 KV 且expirationTtl: 259200030 天形成可回查的执行历史失败则异步推送 Webhook 告警保留失败语义catch 中throw error重新抛出让运行时按 at-least-once 策略自动重试——除非你判断该失败不值得重试见 cron-triggers/api.md 的noRetry()使用场景。查看日志npx wrangler tail实时追踪或在 Cloudflare Dashboard → Workers Pages → Worker → Logs 查看历史。九、Durable Objects 协调Durable Objects Coordination多个 cron 实例含重试产生的重复调度并发执行同一任务时用 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(); } }, };要点说明确定性 IDidFromName(cron-lock)让所有调度实例路由到同一个 Durable Object 实例锁状态天然一致原子抢占tryAcquireLock(scheduledTime)以调度时间为锁粒度重复投递的同一scheduledTime只有第一个能拿到锁优雅放弃拿不到锁时调用controller.noRetry()并直接返回避免无意义的自动重试继续抢锁finally 释放无论任务成败都在finally中releaseLock()防止锁泄漏导致后续调度全部被拒。这种单实例串行化模式特别适合数据库迁移、全局唯一刷新等强一致性场景。十、Python HandlerCloudflare Workers 的 Python 运行时同样支持 cron入口约定为继承WorkerEntrypoint的Default类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)))要点说明签名对应scheduled(self, controller, env, ctx)与 TypeScript 版一一对应controller.cron、controller.scheduledTime、ctx.waitUntil行为一致cron-triggers/api.md 明确说明 Python 与 JS 签名等价绑定一致KV 的get/put、D1 的execute在 Python 侧以异步方式调用后台清理示例用ctx.waitUntil异步删除 7 天前的日志与 TypeScript 版数据库清理模式同构说明本文所有模式均可平移至 Python 运行时。十一、测试模式Testing Patterns本地调试/__scheduled 端点Wrangler 提供/__scheduled测试端点可在本地手动触发任意 cron# 启动开发服务器 npx wrangler dev # 测试指定 cron curl http://localhost:8787/__scheduled?cron*/5**** # 指定具体时间测试scheduledTime 为 Unix 毫秒时间戳 curl http://localhost:8787/__scheduled?cron02***scheduledTime1704067200000查询参数说明源自 cron-triggers/api.mdcron必填URL 编码后的 cron 表达式空格须用代替*/5 * * * *的写法会触发 404这是本地测试最常见的坑详见 gotchas.mdscheduledTime可选Unix 毫秒时间戳缺省为当前时间。生产安全提醒/__scheduled在生产环境同样可用且可被任意人触发。上线前必须在fetch处理器中拦截该路径或做来源校验如校验cf-ray请求头完整拦截代码见 cron-triggers/gotchas.md 的 Security Concerns 章节。单元测试Vitest cloudflare:testCron 处理器是纯对象方法便于用 Vitest 直接构造 mock 调用// 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(); }); });测试要点mock 三件套controller含scheduledTime、cron、type、noRetry、ctx含waitUntil、passThroughOnException与真实绑定env来自cloudflare:test需要cloudflare/vitest-pool-workers支撑见 gotchas.md 的 Testing Best Practices行为断言而非实现断言通过KV 中出现了last_run验证处理器真的执行了业务逻辑而不是断言 mock 被调用了几次幂等场景专项测试第二个用例预置重复执行记录验证noRetry()被调用——这正是对 at-least-once 投递风险的直接回归测试集成测试在 dev 环境用/__scheduled传入重复的scheduledTime验证幂等逻辑生产上线建议从长间隔如*/30 * * * *起步观察 24 小时 Cron Events 并配置告警后再缩短间隔。十二、多调度路由与收尾建议当同一个 Worker 挂多个 cron 表达式时用switch按controller.cron分流完整示例见 cron-triggers/api.md 的 Multiple Schedules 章节switch (controller.cron) { case */3 * * * *: ctx.waitUntil(updateRecentData(env)); break; case 0 * * * *: ctx.waitUntil(processHourlyAggregation(env)); break; case 0 2 * * *: ctx.waitUntil(performDailyMaintenance(env)); break; default: console.warn(Unhandled: ${controller.cron}); }路由模式之外将上文各模式组合成生产级 cron Worker 时请遵循三条底线幂等先行用 KV 记录${controller.cron}-${controller.scheduledTime}作为执行 ID配合expirationTtl: 8640024 小时去重命中重复即noRetry()代码见 gotchas.md 的 Idempotency 章节后台任务兜错ctx.waitUntil中的 promise 务必.catch()显式处理否则失败会静默丢失gotchas.md 的 waitUntil() Tasks Not Completing 章节给出正反例按复杂度拆分 Worker高频同步、日报报表、周级清理各自独立 Worker 部署可获得独立的 CPU 配额、错误隔离与 Green Compute 策略多 Worker 拆分示例与placement: { mode: smart }低碳调度配置见 cron-triggers/configuration.md。深入阅读cron-triggers/README.md — Cron 语法、限额与快速上手cron-triggers/api.md — ScheduledController、noRetry()、waitUntil 与多调度cron-triggers/configuration.md — wrangler.jsonc 配置、环境差异化调度、Green Computecron-triggers/gotchas.md — 时区、幂等、安全与测试排错cloudflare-deploy/SKILL.md — 技能包总览与产品决策树含 Cron Triggers 定位赞分享【免费下载链接】autoskillsOne command. Your entire AI skill stack. Installed.项目地址https://gitcode.com/gh_mirrors/au/autoskills点击查看免费下载相关推荐Onivim 2 中的 Rust 语言支持rust-analyzer 扩展安装与使用指南Onivim 2 中的 Rust 语言支持rust analyzer 扩展安装与使用指南 Onivim 2Oni2是一款原生、轻量的模态代码编辑器其对MediaCrawler 上手指南三步跑通第一次社媒数据采集MediaCrawler 上手指南三步跑通第一次社媒数据采集 MediaCrawler 是一款免费开源的多平台社媒数据采集工具小红书、抖音、快手、B站、微博OpenCore Legacy Patcher 新手指南4 步让旧 Mac 跑起新版 macOSOpenCore Legacy Patcher 新手指南4 步让旧 Mac 跑起新版 macOS 升级窗口弹出这台 Mac 太旧无法安装此版本 macOS操作系统固件驱动开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑