关于 cloudflare-cron-triggers
面向 Cloudflare Workers 开发者的 Cron Triggers 配置与开发指南,涵盖计划任务处理器、Cron 表达式、Wrangler 配置和本地测试。也介绍多计划任务、环境绑定及与 Workflows 配合的模式。
主要功能
- 01配置 scheduled 处理器和 Cron 触发器
- 02编写五字段 Cron 表达式
- 03使用 Wrangler 本地测试
- 04按触发器区分计划任务
- 05将定时任务接入 Workflows
适用场景
- 01按小时或日程运行 Worker 任务
- 02本地验证 scheduled 处理器
- 03为不同任务设置多个执行计划
- 04定时启动多步骤工作流
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill cloudflare-cron-triggers安装后在 Claude Code 中输入/cloudflare-cron-triggers确认已加载。
附带文件README.mdSKILL.json
详细介绍
Cloudflare Cron Triggers
状态:生产就绪 ✅ 最后更新:2025-10-23 依赖项:cloudflare-worker-base(用于 Worker 设置) 最新版本:[email protected]、@cloudflare/[email protected]
快速入门(5 分钟)
1. 向 Worker 添加定时处理程序
src/index.ts:
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
console.log('Cron job executed at:', new Date(controller.scheduledTime));
console.log('Triggered by cron:', controller.cron);
// Your scheduled task logic here
await doPeriodicTask(env);
},
};
这很重要的原因:
- 处理程序必须准确命名为
scheduled(不能是scheduledHandler或onScheduled) - 必须在默认导出对象中导出
- 必须使用 ES 模块格式(不能使用 Service Worker 格式)
2. 在 Wrangler 中配置 Cron Trigger
wrangler.jsonc:
{
"name": "my-scheduled-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-23",
"triggers": {
"crons": [
"0 * * * *" // Every hour at minute 0
]
}
}
重要:
- Cron 表达式使用 5 个字段:
minute hour day-of-month month day-of-week - 所有时间都只能使用 UTC(不支持时区转换)
- 更改最多需要 15 分钟才能传播到全球
3. 在本地测试
# Enable scheduled testing
npx wrangler dev --test-scheduled
# In another terminal, trigger the scheduled handler
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"
# View output in wrangler dev terminal
测试提示:
/__scheduled端点仅在使用--test-scheduled标志时可用- 可以在查询参数中传入任意 Cron 表达式
- Python Workers 使用
/cdn-cgi/handler/scheduled
4. 部署
npm run deploy
# or
npx wrangler deploy
部署后:
- 更改最多可能需要 15 分钟才能传播
- 查看仪表板:Workers & Pages > [Your Worker] > Cron Triggers
- 在 Logs 标签页中查看过去的执行记录
Cron 表达式语法
五字段格式
* * * * *
│ │ │ │ │
│ │ │ │ └─── Day of Week (0-6, Sunday=0)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of Month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)
特殊字符
| 字符 | 含义 | 示例 |
|---|---|---|
* | 每个 | * * * * * = 每分钟 |
, | 列表 | 0,30 * * * * = 每小时的 :00 和 :30 |
- | 范围 | 0 9-17 * * * = 每天上午 9 点到下午 5 点之间的每小时 |
/ | 步长 | */15 * * * * = 每 15 分钟 |
常用模式
# Every minute
* * * * *
# Every 5 minutes
*/5 * * * *
# Every 15 minutes
*/15 * * * *
# Every hour at minute 0
0 * * * *
# Every hour at minute 30
30 * * * *
# Every 6 hours
0 */6 * * *
# Every day at midnight (00:00 UTC)
0 0 * * *
# Every day at noon (12:00 UTC)
0 12 * * *
# Every day at 3:30am UTC
30 3 * * *
# Every Monday at 9am UTC
0 9 * * 1
# Every weekday at 9am UTC
0 9 * * 1-5
# Every Sunday at midnight UTC
0 0 * * 0
# First day of every month at midnight UTC
0 0 1 * *
# Twice a day (6am and 6pm UTC)
0 6,18 * * *
# Every 30 minutes during business hours (9am-5pm UTC, weekdays)
*/30 9-17 * * 1-5
重要:仅支持 UTC 时区
- 所有 Cron Trigger 均按 UTC 时间执行
- 不支持时区转换
- 请手动将本地时间转换为 UTC
- 示例:PST 上午 9 点 = UTC 下午 5 点(夏令时期间为次日)
ScheduledController 接口
interface ScheduledController {
readonly cron: string; // The cron expression that triggered this execution
readonly type: string; // Always "scheduled"
readonly scheduledTime: number; // Unix timestamp (ms) when scheduled
}
属性
controller.cron(string)
触发此次执行的 Cron 表达式。
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
console.log(`Triggered by: ${controller.cron}`);
// Output: "Triggered by: 0 * * * *"
},
};
**使用场景:**区分多个 Cron 计划(参见“多个 Cron Trigger”模式)。
controller.type(string)
Cron 触发的执行始终返回 "scheduled"。
if (controller.type === 'scheduled') {
// This is a cron-triggered execution
}
controller.scheduledTime(number)
计划执行时间的 Unix 时间戳(自纪元起的毫秒数)。
export default {
async scheduled(controller: ScheduledController): Promise<void> {
const scheduledDate = new Date(controller.scheduledTime);
console.log(`Scheduled for: ${scheduledDate.toISOString()}`);
// Output: "Scheduled for: 2025-10-23T15:00:00.000Z"
},
};
注意:这是计划执行时间,而非实际执行时间。受系统负载影响,实际执行可能略有延迟(通常小于 1 秒)。
执行上下文
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext // ← Execution context
): Promise<void> {
// Use ctx.waitUntil() for async operations that should complete
ctx.waitUntil(logToAnalytics(env));
},
};
ctx.waitUntil(promise: Promise<any>)
扩展执行上下文,使处理程序返回后仍会等待异步操作完成。
使用场景:
- 向外部服务记录日志
- 跟踪分析数据
- 清理操作
- 非关键后台任务
export default {
async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext): Promise<void> {
// Critical task - must complete before handler exits
await processData(env);
// Non-critical tasks - can complete in background
ctx.waitUntil(sendMetrics(env));
ctx.waitUntil(cleanupOldData(env));
ctx.waitUntil(notifySlack({ message: 'Cron completed' }));
},
};
**重要:**第一个失败的 waitUntil() 将作为状态记录在仪表板日志中。
集成模式
1. 独立的定时 Worker
**适用场景:**仅按计划运行的 Worker(不处理 HTTP 请求)
// src/index.ts
interface Env {
DB: D1Database;
MY_BUCKET: R2Bucket;
}
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
console.log('Running scheduled maintenance...');
// Database cleanup
await env.DB.prepare('DELETE FROM sessions WHERE expires_at < ?')
.bind(Date.now())
.run();
// Generate daily report
const report = await generateDailyReport(env.DB);
// Upload to R2
await env.MY_BUCKET.put(
`reports/${new Date().toISOString().split('T')[0]}.json`,
JSON.stringify(report)
);
console.log('Maintenance complete');
},
};
2. 与 Hono 结合(Fetch + Scheduled)
**适用场景:**同时处理 HTTP 请求和定时任务的 Worker
// src/index.ts
import { Hono } from 'hono';
interface Env {
DB: D1Database;
}
const app = new Hono<{ Bindings: Env }>();
// Regular HTTP routes
app.get('/', (c) => c.text('Worker is running'));
app.get('/api/stats', async (c) => {
const stats = await c.env.DB.prepare('SELECT COUNT(*) as count FROM users').first();
return c.json(stats);
});
// Export both fetch handler and scheduled handler
export default {
// Handle HTTP requests
fetch: app.fetch,
// Handle cron triggers
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
console.log('Cron triggered:', controller.cron);
// Run scheduled task
await updateCache(env.DB);
// Log completion
ctx.waitUntil(logExecution(controller.scheduledTime));
},
};
采用此模式的原因:
- 一个 Worker 同时处理两种使用场景
- 共享环境绑定
- 减少需要管理的 Worker 数量
- 降低成本(只需一个 Worker 订阅)
3. 多个 Cron Trigger
**适用场景:**为不同任务设置不同计划
wrangler.jsonc:
{
"triggers": {
"crons": [
"*/5 * * * *", // Every 5 minutes
"0 */6 * * *", // Every 6 hours
"0 0 * * *" // Daily at midnight UTC
]
}
}
src/index.ts:
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
// Route based on which cron triggered this execution
switch (controller.cron) {
case '*/5 * * * *':
// Every 5 minutes: Check system health
await checkSystemHealth(env);
break;
case '0 */6 * * *':
// Every 6 hours: Sync data from external API
await syncExternalData(env);
break;
case '0 0 * * *':
// Daily at midnight: Generate reports and cleanup
await generateDailyReports(env);
await cleanupOldData(env);
break;
default:
console.warn(`Unknown cron trigger: ${controller.cron}`);
}
},
};
重要:
- 使用完全匹配的 Cron 表达式(空白字符也必须匹配)
- 每个 Worker 最多支持 3 个 Cron Trigger(免费计划)
- Standard/Paid 计划支持更多(请查看限制)
4. 访问环境绑定
定时处理程序可使用所有 Worker 绑定:
interface Env {
// Databases
DB: D1Database;
// Storage
MY_BUCKET: R2Bucket;
KV_NAMESPACE: KVNamespace;
// AI & Vectors
AI: Ai;
VECTOR_INDEX: VectorizeIndex;
// Queues & Workflows
MY_QUEUE: Queue;
MY_WORKFLOW: Workflow;
// Durable Objects
RATE_LIMITER: DurableObjectNamespace;
// Secrets
API_KEY: string;
}
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
// D1 Database
const users = await env.DB.prepare('SELECT * FROM users WHERE active = 1').all();
// R2 Storage
const file = await env.MY_BUCKET.get('data.json');
// KV Storage
const config = await env.KV_NAMESPACE.get('config', 'json');
// Workers AI
const response = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
prompt: 'Summarize today\'s data',
});
// Send to Queue
await env.MY_QUEUE.send({ type: 'process', data: users.results });
// Trigger Workflow
await env.MY_WORKFLOW.create({ input: { timestamp: Date.now() } });
// Use secrets
await fetch('https://api.example.com/webhook', {
headers: { Authorization: `Bearer ${env.API_KEY}` },
});
},
};
5. 与工作流结合
最适合: 按计划触发的多步骤长时间运行任务
wrangler.jsonc:
{
"triggers": {
"crons": ["0 2 * * *"] // Daily at 2am UTC
},
"workflows": [
{
"name": "daily-report-workflow",
"binding": "DAILY_REPORT"
}
]
}
src/index.ts:
interface Env {
DAILY_REPORT: Workflow;
}
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
console.log('Triggering daily report workflow...');
// Trigger workflow with initial state
const instance = await env.DAILY_REPORT.create({
params: {
date: new Date().toISOString().split('T')[0],
reportType: 'daily-summary',
},
});
console.log(`Workflow started: ${instance.id}`);
},
};
为什么使用工作流:
- 工作流可以运行数小时(cron 处理程序有 CPU 限制)
- 内置重试和错误处理
- 跨步骤持久化状态
- 更适合复杂的多步骤流程
6. 在定时处理程序中进行错误处理
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
try {
// Main task
await performScheduledTask(env);
} catch (error) {
// Log error
console.error('Scheduled task failed:', error);
// Send alert
await sendAlert({
worker: 'my-scheduled-worker',
cron: controller.cron,
error: error.message,
timestamp: new Date(controller.scheduledTime).toISOString(),
});
// Store failure in database
ctx.waitUntil(
env.DB.prepare(
'INSERT INTO cron_failures (cron, error, timestamp) VALUES (?, ?, ?)'
)
.bind(controller.cron, error.message, Date.now())
.run()
);
// Re-throw to mark execution as failed
throw error;
}
},
};
async function sendAlert(details: any): Promise<void> {
await fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `🚨 Cron job failed: ${details.worker}`,
blocks: [
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*Worker:*\n${details.worker}` },
{ type: 'mrkdwn', text: `*Cron:*\n${details.cron}` },
{ type: 'mrkdwn', text: `*Error:*\n${details.error}` },
{ type: 'mrkdwn', text: `*Time:*\n${details.timestamp}` },
],
},
],
}),
});
}
Wrangler 配置
基本配置
{
"name": "my-scheduled-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-23",
"triggers": {
"crons": ["0 * * * *"]
}
}
多个 Cron 触发器
{
"triggers": {
"crons": [
"*/5 * * * *", // Every 5 minutes
"0 */6 * * *", // Every 6 hours
"0 2 * * *", // Daily at 2am UTC
"0 0 * * 1" // Weekly on Monday at midnight UTC
]
}
}
限制:
- 免费版:最多 3 个 cron 计划
- 付费版:限制更高(请查看当前限制)
特定环境的 Cron
{
"name": "my-worker",
"main": "src/index.ts",
"env": {
"dev": {
"triggers": {
"crons": ["*/5 * * * *"] // Dev: every 5 minutes for testing
}
},
"staging": {
"triggers": {
"crons": ["*/30 * * * *"] // Staging: every 30 minutes
}
},
"production": {
"triggers": {
"crons": ["0 * * * *"] // Production: hourly
}
}
}
}
部署特定环境:
# Deploy to dev
npx wrangler deploy --env dev
# Deploy to production
npx wrangler deploy --env production
移除所有 Cron 触发器
{
"triggers": {
"crons": [] // Empty array removes all crons
}
}
部署后,Worker 将不再按计划执行。
测试与开发
使用 Wrangler 进行本地测试
# Start dev server with scheduled testing enabled
npx wrangler dev --test-scheduled
这会开放 /__scheduled 端点,用于触发定时处理程序。
触发定时处理程序
# Trigger with default cron (if only one configured)
curl "http://localhost:8787/__scheduled"
# Trigger with specific cron expression
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"
# Trigger with URL-encoded cron
curl "http://localhost:8787/__scheduled?cron=*/5+*+*+*+*"
注意: 在 URL 中使用 + 代替空格,或正确进行 URL 编码。
验证处理程序输出
# Start dev server
npx wrangler dev --test-scheduled
# In another terminal, trigger and watch output
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"
输出会显示在 wrangler dev 终端中:
[wrangler:inf] GET /__scheduled?cron=0+*+*+*+* 200 OK (45ms)
Cron job executed at: 2025-10-23T15:00:00.000Z
Triggered by cron: 0 * * * *
Scheduled task completed successfully
测试多个 Cron 表达式
# Test hourly cron
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"
# Test daily cron
curl "http://localhost:8787/__scheduled?cron=0+0+*+*+*"
# Test weekly cron
curl "http://localhost:8787/__scheduled?cron=0+0+*+*+1"
测试 Python Workers
# Python Workers use different endpoint
curl "http://localhost:8787/cdn-cgi/handler/scheduled?cron=*+*+*+*+*"
绿色计算
仅在由可再生能源供电的数据中心运行 cron 触发器。
启用绿色计算
通过控制面板:
- 前往 Workers & Pages
- 在 Account details 部分中找到 Compute Setting
- 点击 Change
- 选择 Green Compute
- 点击 Confirm
适用范围:
- 账户中的所有 cron 触发器
- 减少碳足迹
- 不产生额外费用
- 可能会导致某些地区出现轻微延迟
工作原理:
- Cloudflare 将 cron 执行路由到由绿色能源供电的数据中心
- 使用可再生能源:风能、太阳能、水力发电
- 通过购电协议(PPAs)和可再生能源证书(RECs)进行验证
已知问题预防
此技能可预防 6 个已记录的问题:
问题 #1:Cron 更改未生效
错误: wrangler.jsonc 中的 cron 触发器已更新,但未执行
原因:
- cron 触发器的更改最多需要 15 分钟 才能在全球范围内生效
- Cloudflare 网络需要时间更新边缘节点
- 不会像常规部署一样即时生效
预防方法:
- 部署后等待 15 分钟,再确认其是否执行
- 检查控制面板:Workers & Pages > [Worker] > Cron Triggers
- 仅更改触发器时,使用
wrangler triggers deploy
# If you only changed triggers (not code), use:
npx wrangler triggers deploy
# Wait 15 minutes, then verify in dashboard
问题 #2:处理程序未导出
错误: Handler does not export a 'scheduled' method
来源: 常见部署错误
原因:
- 处理程序的名称不完全是
scheduled - 处理程序未在默认导出对象中导出
- 使用的是 Service Worker 格式,而不是 ES 模块
预防方法:
// ❌ Wrong: Incorrect handler name
export default {
async scheduledHandler(controller, env, ctx) { }
};
// ❌ Wrong: Not in default export
export async function scheduled(controller, env, ctx) { }
// ✅ Correct: Named 'scheduled' in default export
export default {
async scheduled(controller, env, ctx) { }
};
问题 #3:UTC 时区混淆
错误: Cron 在错误的时间运行
来源: 用户预期与实际情况不符
原因:
- 所有 cron 触发器仅按 UTC 时间 运行
- 不支持时区转换
- 用户预期使用本地时区
预防方法:
手动将本地时间转换为 UTC:
// Want to run at 9am PST (UTC-8)?
// 9am PST = 5pm UTC (17:00)
{
"triggers": {
"crons": ["0 17 * * *"] // 9am PST = 5pm UTC
}
}
// Want to run at 6pm EST (UTC-5)?
// 6pm EST = 11pm UTC (23:00)
{
"triggers": {
"crons": ["0 23 * * *"] // 6pm EST = 11pm UTC
}
}
// Remember: DST changes affect conversion!
// PST is UTC-8, PDT is UTC-7
工具:
问题 #4:Cron 表达式无效
错误: Cron 未执行,且未显示错误
来源: 验证失败但未提示
发生原因:
- 无效的 Cron 语法会静默失败
- 验证发生在部署时,但可能不易察觉
- 常见错误:字段顺序错误、范围无效
预防措施:
# ❌ Wrong: Too many fields (6 fields instead of 5)
"crons": ["0 0 * * * *"] # Has seconds field - not supported
# ❌ Wrong: Invalid minute range
"crons": ["65 * * * *"] # Minute must be 0-59
# ❌ Wrong: Invalid day of week
"crons": ["0 0 * * 7"] # Day of week is 0-6 (use 0 for Sunday)
# ✅ Correct: 5 fields, valid ranges
"crons": ["0 0 * * 0"] # Sunday at midnight UTC
验证:
- 使用 Crontab Guru 验证表达式
- 检查 wrangler deploy 输出中的错误
- 使用
--test-scheduled在本地测试
问题 #5:缺少 ES 模块格式
错误: Worker must use ES modules format
来源: 旧版 Service Worker 格式
发生原因:
- 定时处理程序要求使用 ES 模块格式
- 不支持旧版 Service Worker 格式
- 代码库中混用了不同格式
预防措施:
// ❌ Wrong: Service Worker format
addEventListener('scheduled', (event) => {
event.waitUntil(handleScheduled(event));
});
// ✅ Correct: ES modules format
export default {
async scheduled(controller, env, ctx) {
await handleScheduled(controller, env, ctx);
},
};
问题 #6:超出 CPU 时间限制
错误: CPU time limit exceeded
来源: 长时间运行的定时任务
发生原因:
- 默认 CPU 时间限制:30 秒
- 长时间运行的任务会超出限制
- 不会自动延长超时时间
预防措施:
选项 1:在 wrangler.jsonc 中提高 CPU 时间限制
{
"limits": {
"cpu_ms": 300000 // 5 minutes (max for Standard plan)
}
}
选项 2:对长时间运行的任务使用 Workflows
// Instead of long task in cron:
export default {
async scheduled(controller, env, ctx) {
// Trigger Workflow that can run for hours
await env.MY_WORKFLOW.create({
params: { task: 'long-running-job' },
});
},
};
选项 3:拆分为更小的任务块
export default {
async scheduled(controller, env, ctx) {
// Process in batches
const batch = await getNextBatch(env.DB);
for (const item of batch) {
await processItem(item);
}
// If more work, send to Queue for next batch
const hasMore = await hasMoreWork(env.DB);
if (hasMore) {
await env.MY_QUEUE.send({ type: 'continue-processing' });
}
},
};
始终要做 ✅
- 使用准确的处理程序名称 - 必须是
scheduled,而不是scheduledHandler或其变体 - 使用 ES 模块格式 - 在默认对象中导出,而不是使用 addEventListener
- 转换为 UTC - 所有 Cron 时间均为 UTC,请从本地时区转换
- 等待 15 分钟 - Cron 更改最多需要 15 分钟才能生效
- 先在本地测试 - 使用
wrangler dev --test-scheduled - 验证 Cron 语法 - 使用 Crontab Guru
- 妥善处理错误 - 记录日志、发送警报,并可选择重新抛出错误
- 使用 ctx.waitUntil() - 用于非关键的异步操作
- 考虑使用 Workflows - 适用于需要超过 30 秒 CPU 时间的任务
- 监控执行情况 - 定期检查仪表板日志
绝不要做 ❌
- 绝不要假定使用本地时区 - 所有 Cron 均按 UTC 运行
- 绝不要使用 6 字段 Cron 表达式 - Cloudflare 使用 5 字段格式(不含秒)
- 绝不要依赖立即生效 - 更改最多需要 15 分钟才能生效
- 绝不要使用 Service Worker 格式 - 必须使用 ES 模块格式
- 绝不要忘记错误处理 - 未捕获的错误会静默导致失败
- 绝不要在未提高限制时运行 CPU 密集型任务 - 默认限制为 30 秒
- 绝不要使用星期字段值 7 - 星期日使用 0(仅限 0–6 范围)
- 绝不要未经测试就部署 - 始终先使用
--test-scheduled测试 - 绝不要忽略执行日志 - 仪表板会显示过去的失败记录
- 绝不要为测试硬编码计划 - 使用特定环境的配置
常见用例
1. 数据库清理
每天 UTC 时间凌晨 2 点:删除旧记录
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
// Delete sessions older than 30 days
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
await env.DB.prepare('DELETE FROM sessions WHERE created_at < ?')
.bind(thirtyDaysAgo)
.run();
// Delete soft-deleted users older than 90 days
const ninetyDaysAgo = Date.now() - (90 * 24 * 60 * 60 * 1000);
await env.DB.prepare('DELETE FROM users WHERE deleted_at < ?')
.bind(ninetyDaysAgo)
.run();
console.log('Database cleanup completed');
},
};
wrangler.jsonc:
{
"triggers": {
"crons": ["0 2 * * *"] // Daily at 2am UTC
}
}
2. API 数据收集
每 15 分钟:从外部 API 获取数据
interface Env {
DB: D1Database;
API_KEY: string;
}
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
try {
// Fetch from external API
const response = await fetch('https://api.example.com/v1/data', {
headers: {
Authorization: `Bearer ${env.API_KEY}`,
},
});
const data = await response.json();
// Store in D1
for (const item of data.items) {
await env.DB.prepare(
'INSERT INTO collected_data (id, value, timestamp) VALUES (?, ?, ?)'
)
.bind(item.id, item.value, Date.now())
.run();
}
console.log(`Collected ${data.items.length} items`);
} catch (error) {
console.error('Failed to collect data:', error);
throw error; // Mark execution as failed
}
},
};
wrangler.jsonc:
{
"triggers": {
"crons": ["*/15 * * * *"] // Every 15 minutes
}
}
3. 每日报告生成
每天 UTC 时间上午 8 点:生成并通过电子邮件发送报告
export default {
async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext): Promise<void> {
// Generate report from database
const report = await generateDailyReport(env.DB);
// Store in R2
const fileName = `reports/${new Date().toISOString().split('T')[0]}.json`;
await env.MY_BUCKET.put(fileName, JSON.stringify(report));
// Send via email
ctx.waitUntil(sendReportEmail(report, env.RESEND_API_KEY));
console.log('Daily report generated and sent');
},
};
async function generateDailyReport(db: D1Database) {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
const startOfDay = yesterday.setHours(0, 0, 0, 0);
const endOfDay = yesterday.setHours(23, 59, 59, 999);
const stats = await db
.prepare(`
SELECT
COUNT(*) as total_users,
COUNT(DISTINCT user_id) as active_users,
SUM(revenue) as total_revenue
FROM events
WHERE timestamp BETWEEN ? AND ?
`)
.bind(startOfDay, endOfDay)
.first();
return {
date: yesterday.toISOString().split('T')[0],
stats,
};
}
wrangler.jsonc:
{
"triggers": {
"crons": ["0 8 * * *"] // Daily at 8am UTC
}
}
4. 缓存预热
每小时:使用热门内容预热缓存
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
// Get most popular pages from analytics
const popularPages = await env.DB
.prepare('SELECT url FROM pages ORDER BY views DESC LIMIT 100')
.all();
// Fetch each page to warm cache
const requests = popularPages.results.map((page) =>
fetch(`https://example.com${page.url}`, {
cf: {
cacheTtl: 3600, // Cache for 1 hour
},
})
);
await Promise.all(requests);
console.log(`Warmed cache for ${popularPages.results.length} pages`);
},
};
wrangler.jsonc:
{
"triggers": {
"crons": ["0 * * * *"] // Every hour
}
}
5. 监控与运行状况检查
每 5 分钟:检查系统运行状况
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
const checks = await Promise.allSettled([
checkDatabaseHealth(env.DB),
checkAPIHealth(),
checkStorageHealth(env.MY_BUCKET),
]);
const failures = checks.filter((check) => check.status === 'rejected');
if (failures.length > 0) {
// Send alert
await sendAlert({
service: 'health-check',
failures: failures.map((f) => f.reason),
timestamp: new Date().toISOString(),
});
}
},
};
async function checkDatabaseHealth(db: D1Database): Promise<void> {
const result = await db.prepare('SELECT 1 as health').first();
if (!result || result.health !== 1) {
throw new Error('Database health check failed');
}
}
async function checkAPIHealth(): Promise<void> {
const response = await fetch('https://api.example.com/health');
if (!response.ok) {
throw new Error(`API health check failed: ${response.status}`);
}
}
async function checkStorageHealth(bucket: R2Bucket): Promise<void> {
const testObject = await bucket.get('health-check.txt');
if (!testObject) {
throw new Error('Storage health check failed');
}
}
wrangler.jsonc:
{
"triggers": {
"crons": ["*/5 * * * *"] // Every 5 minutes
}
}
TypeScript 类型
// Scheduled event controller
interface ScheduledController {
readonly cron: string;
readonly type: string;
readonly scheduledTime: number;
}
// Execution context
interface ExecutionContext {
waitUntil(promise: Promise<any>): void;
passThroughOnException(): void;
}
// Scheduled handler
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void>;
}
限制与定价
限制
| 功能 | 免费计划 | 付费计划 |
|---|---|---|
| 每个 Worker 的 Cron 触发器数量 | 3 | 更多(请查阅文档) |
| 每次执行的 CPU 时间 | 10 ms(平均) | 30 秒(默认),5 分钟(最大) |
| 墙钟时间 | 30 秒 | 15 分钟 |
| 内存 | 128 MB | 128 MB |
定价
Cron 触发器使用 Standard Workers 定价:
- Workers Paid Plan:每月需要支付 $5
- 请求:每百万个请求 $0.30(超过 1,000 万次免费额度后)
- CPU 时间:每百万个 CPU-ms $0.02(超过 3,000 万次免费额度后)
Cron 执行 = 1 个请求
示例:
- Cron 每小时运行一次(每天 24 次)
- 30 天 × 24 次执行 = 每月 720 次执行
- 每次执行平均使用 50ms CPU 时间
费用:
- 请求:720 次(远低于 1,000 万次免费额度)
- CPU 时间:720 × 50ms = 36,000ms(低于 3,000 万次免费额度)
- 总计:每月 $5(仅订阅费用)
高频率示例:
- Cron 每分钟运行一次(每天 1,440 次)
- 30 天 × 1,440 次 = 每月 43,200 次执行
- 仍低于免费额度
- 总计:每月 $5
故障排除
问题:Cron 未执行
可能原因:
- 更改尚未生效(等待 15 分钟)
- Cron 表达式无效
- 处理函数未正确导出
- Worker 尚未部署
解决方案:
# Re-deploy
npx wrangler deploy
# Wait 15 minutes
# Check dashboard
# Workers & Pages > [Worker] > Cron Triggers
# Check logs
# Workers & Pages > [Worker] > Logs > Real-time Logs
问题:处理函数已执行,但失败了
可能原因:
- 处理函数中存在未捕获的错误
- 超出 CPU 时间限制
- 缺少环境绑定
- 网络超时
解决方案:
export default {
async scheduled(controller, env, ctx) {
try {
await yourTask(env);
} catch (error) {
// Log detailed error
console.error('Handler failed:', {
error: error.message,
stack: error.stack,
cron: controller.cron,
time: new Date(controller.scheduledTime),
});
// Send alert
ctx.waitUntil(sendAlert(error));
// Re-throw to mark as failed
throw error;
}
},
};
在控制面板中查看日志,了解错误详情。
问题:执行时间错误
原因: 混淆了 UTC 和本地时区
解决方案:
将所需的本地时间转换为 UTC:
// Want 9am PST (UTC-8)?
// 9am PST = 5pm UTC (17:00)
{
"triggers": {
"crons": ["0 17 * * *"]
}
}
工具:
- 世界时钟转换器
- 请注意夏令时变化(PST 与 PDT)
问题:本地测试无法运行
可能原因:
- 缺少
--test-scheduled标志 - 端点错误(应为
/__scheduled) - Python Worker(请使用
/cdn-cgi/handler/scheduled)
解决方案:
# Correct: Start with flag
npx wrangler dev --test-scheduled
# In another terminal
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"
生产环境检查清单
将 Cron 触发器部署到生产环境前:
- 已在 Crontab Guru 上验证 Cron 表达式
- 默认导出中的处理函数名称准确为
scheduled - 使用 ES 模块格式(而非 Service Worker)
- 已将本地时区转换为 UTC
- 已实现带日志记录的错误处理
- 已配置失败告警
- 如有需要,已提高 CPU 限制(
limits.cpu_ms) - 已测试环境绑定
- 已使用
--test-scheduled在本地测试 - 已在预发布环境中测试部署
- 部署后已等待 15 分钟以便更改生效
- 已在控制面板日志中确认执行情况
- 已配置监控和告警
- 已更新包含计划详情的文档
相关文档
- Cloudflare Cron 触发器:https://developers.cloudflare.com/workers/configuration/cron-triggers/
- 计划处理函数 API:https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/
- Cron 触发器示例:https://developers.cloudflare.com/workers/examples/cron-trigger/
- 多个 Cron 触发器:https://developers.cloudflare.com/workers/examples/multiple-cron-triggers/
- Wrangler 触发器命令:https://developers.cloudflare.com/workers/wrangler/commands/#triggers
- Workers 定价:https://developers.cloudflare.com/workers/platform/pricing/
- Workflows 集成:https://developers.cloudflare.com/workflows/
- Crontab Guru(验证器):https://crontab.guru/
- 时区转换器:https://www.timeanddate.com/worldclock/converter.html
最后更新:2025-10-23 版本:1.0.0 维护者:Jeremy Dawes | [email protected]