cloudflare-cron-triggers
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCloudflare Cron Triggers
Cloudflare Cron Triggers
Status: Production Ready ✅
Last Updated: 2025-10-23
Dependencies: cloudflare-worker-base (for Worker setup)
Latest Versions: wrangler@4.43.0, @cloudflare/workers-types@4.20251014.0
状态: 已可生产使用 ✅
最后更新: 2025-10-23
依赖: cloudflare-worker-base(用于 Worker 搭建)
最新版本: wrangler@4.43.0, @cloudflare/workers-types@4.20251014.0
Quick Start (5 Minutes)
快速入门(5分钟)
1. Add Scheduled Handler to Your Worker
1. 为你的 Worker 添加定时处理器
src/index.ts:
typescript
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);
},
};Why this matters:
- Handler must be named exactly (not
scheduledorscheduledHandler)onScheduled - Must be exported in default export object
- Must use ES modules format (not Service Worker format)
src/index.ts:
typescript
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);
// 你的定时任务逻辑写在这里
await doPeriodicTask(env);
},
};注意要点:
- 处理器必须准确命名为 (不能是
scheduled或scheduledHandler)onScheduled - 必须在默认导出对象中导出
- 必须使用 ES modules 格式(不能用 Service Worker 格式)
2. Configure Cron Trigger in Wrangler
2. 在 Wrangler 中配置 Cron 触发器
wrangler.jsonc:
jsonc
{
"name": "my-scheduled-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-23",
"triggers": {
"crons": [
"0 * * * *" // Every hour at minute 0
]
}
}CRITICAL:
- Cron expressions use 5 fields:
minute hour day-of-month month day-of-week - All times are UTC only (no timezone conversion)
- Changes take up to 15 minutes to propagate globally
wrangler.jsonc:
jsonc
{
"name": "my-scheduled-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-23",
"triggers": {
"crons": [
"0 * * * *" // 每小时第0分钟执行
]
}
}重要提示:
- Cron 表达式使用5个字段:
分钟 小时 日期 月份 星期 - 所有时间仅支持UTC时区(无自动时区转换)
- 配置变更最多需要15分钟完成全球节点同步
3. Test Locally
3. 本地测试
bash
undefinedbash
undefinedEnable scheduled testing
开启定时任务测试能力
npx wrangler dev --test-scheduled
npx wrangler dev --test-scheduled
In another terminal, trigger the scheduled handler
在另一个终端触发定时处理器
View output in wrangler dev terminal
在 wrangler dev 终端查看输出
**Testing tips:**
- `/__scheduled` endpoint is only available with `--test-scheduled` flag
- Can pass any cron expression in query parameter
- Python Workers use `/cdn-cgi/handler/scheduled` instead
**测试提示:**
- `/__scheduled` 端点仅在开启 `--test-scheduled` 标志时可用
- 可在查询参数中传入任意 cron 表达式
- Python Worker 请使用 `/cdn-cgi/handler/scheduled` 端点4. Deploy
4. 部署
bash
npm run deploybash
npm run deployor
或者
npx wrangler deploy
**After deployment:**
- Changes may take up to 15 minutes to propagate
- Check dashboard: Workers & Pages > [Your Worker] > **Cron Triggers**
- View past executions in **Logs** tab
---npx wrangler deploy
**部署后操作:**
- 变更最多需要15分钟完成同步
- 可在控制台查看:Workers & Pages > [你的Worker] > **Cron Triggers**
- 在**日志**标签页查看历史执行记录
---Cron Expression Syntax
Cron 表达式语法
Five-Field Format
五字段格式
* * * * *
│ │ │ │ │
│ │ │ │ └─── Day of Week (0-6, Sunday=0)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of Month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)* * * * *
│ │ │ │ │
│ │ │ │ └─── 星期 (0-6, 周日=0)
│ │ │ └───── 月份 (1-12)
│ │ └─────── 日期 (1-31)
│ └───────── 小时 (0-23)
└─────────── 分钟 (0-59)Special Characters
特殊字符
| Character | Meaning | Example |
|---|---|---|
| Every | |
| List | |
| Range | |
| Step | |
| 字符 | 含义 | 示例 |
|---|---|---|
| 所有值 | |
| 枚举值 | |
| 范围值 | |
| 步长 | |
Common Patterns
常用表达式示例
bash
undefinedbash
undefinedEvery minute
每分钟执行
Every 5 minutes
每5分钟执行
*/5 * * * *
*/5 * * * *
Every 15 minutes
每15分钟执行
*/15 * * * *
*/15 * * * *
Every hour at minute 0
每小时第0分钟执行
0 * * * *
0 * * * *
Every hour at minute 30
每小时第30分钟执行
30 * * * *
30 * * * *
Every 6 hours
每6小时执行
0 */6 * * *
0 */6 * * *
Every day at midnight (00:00 UTC)
每天UTC零点执行
0 0 * * *
0 0 * * *
Every day at noon (12:00 UTC)
每天UTC正午执行
0 12 * * *
0 12 * * *
Every day at 3:30am UTC
每天UTC凌晨3:30执行
30 3 * * *
30 3 * * *
Every Monday at 9am UTC
每周一UTC上午9点执行
0 9 * * 1
0 9 * * 1
Every weekday at 9am UTC
每个工作日UTC上午9点执行
0 9 * * 1-5
0 9 * * 1-5
Every Sunday at midnight UTC
每周日UTC零点执行
0 0 * * 0
0 0 * * 0
First day of every month at midnight UTC
每月1号UTC零点执行
0 0 1 * *
0 0 1 * *
Twice a day (6am and 6pm UTC)
每天两次(UTC6点和18点)
0 6,18 * * *
0 6,18 * * *
Every 30 minutes during business hours (9am-5pm UTC, weekdays)
工作日UTC9点到17点每30分钟执行
*/30 9-17 * * 1-5
**CRITICAL: UTC Timezone Only**
- All cron triggers execute on **UTC time**
- No timezone conversion available
- Convert your local time to UTC manually
- Example: 9am PST = 5pm UTC (next day during DST)
---*/30 9-17 * * 1-5
**重要提示:仅支持UTC时区**
- 所有 cron 触发器都按**UTC时间**执行
- 无内置时区转换能力
- 请手动将本地时间转换为UTC时间
- 示例:太平洋标准时间上午9点 = UTC下午5点(夏令时期间会延后一天)
---ScheduledController Interface
ScheduledController 接口
typescript
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
}typescript
interface ScheduledController {
readonly cron: string; // 触发本次执行的cron表达式
readonly type: string; // 固定为"scheduled"
readonly scheduledTime: number; // 预定执行时间的Unix时间戳(毫秒)
}Properties
属性说明
controller.cron
(string)
controller.croncontroller.cron
(字符串)
controller.cronThe cron expression that triggered this execution.
typescript
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
console.log(`Triggered by: ${controller.cron}`);
// Output: "Triggered by: 0 * * * *"
},
};Use case: Differentiate between multiple cron schedules (see Multiple Cron Triggers pattern).
触发本次执行的 cron 表达式。
typescript
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
console.log(`Triggered by: ${controller.cron}`);
// 输出: "Triggered by: 0 * * * *"
},
};适用场景: 区分多个 cron 调度规则(参考多 Cron 触发器模式)。
controller.type
(string)
controller.typecontroller.type
(字符串)
controller.typeAlways returns for cron-triggered executions.
"scheduled"typescript
if (controller.type === 'scheduled') {
// This is a cron-triggered execution
}对于 cron 触发的执行,固定返回 。
"scheduled"typescript
if (controller.type === 'scheduled') {
// 这是 cron 触发的执行
}controller.scheduledTime
(number)
controller.scheduledTimecontroller.scheduledTime
(数字)
controller.scheduledTimeUnix timestamp (milliseconds since epoch) when this execution was scheduled to run.
typescript
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"
},
};Note: This is the scheduled time, not the actual execution time. Due to system load, actual execution may be slightly delayed (usually <1 second).
本次执行预定触发时间的 Unix 时间戳(从 epoch 开始的毫秒数)。
typescript
export default {
async scheduled(controller: ScheduledController): Promise<void> {
const scheduledDate = new Date(controller.scheduledTime);
console.log(`Scheduled for: ${scheduledDate.toISOString()}`);
// 输出: "Scheduled for: 2025-10-23T15:00:00.000Z"
},
};注意: 这是预定执行时间,不是实际执行时间。受系统负载影响,实际执行可能会有小幅延迟(通常小于1秒)。
Execution Context
执行上下文
typescript
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));
},
};typescript
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext // ← 执行上下文
): Promise<void> {
// 对于需要确保完成的异步操作使用 ctx.waitUntil()
ctx.waitUntil(logToAnalytics(env));
},
};ctx.waitUntil(promise: Promise<any>)
ctx.waitUntil(promise: Promise<any>)ctx.waitUntil(promise: Promise<any>)
ctx.waitUntil(promise: Promise<any>)Extends the execution context to wait for async operations to complete after the handler returns.
Use cases:
- Logging to external services
- Analytics tracking
- Cleanup operations
- Non-critical background tasks
typescript
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' }));
},
};Important: First that fails will be reported as the status in dashboard logs.
waitUntil()延长执行上下文的生命周期,确保处理器返回后异步操作仍能完成。
适用场景:
- 上报日志到外部服务
- 统计数据埋点
- 清理操作
- 非关键后台任务
typescript
export default {
async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext): Promise<void> {
// 关键任务 - 必须在处理器退出前完成
await processData(env);
// 非关键任务 - 可在后台执行完成
ctx.waitUntil(sendMetrics(env));
ctx.waitUntil(cleanupOldData(env));
ctx.waitUntil(notifySlack({ message: 'Cron completed' }));
},
};重要提示: 第一个执行失败的 会被作为执行状态上报到控制台日志。
waitUntil()Integration Patterns
集成模式
1. Standalone Scheduled Worker
1. 独立定时 Worker
Best for: Workers that only run on schedule (no HTTP requests)
typescript
// 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');
},
};适用场景: 仅需要定时运行、无需处理HTTP请求的Worker
typescript
// 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...');
// 数据库清理
await env.DB.prepare('DELETE FROM sessions WHERE expires_at < ?')
.bind(Date.now())
.run();
// 生成每日报表
const report = await generateDailyReport(env.DB);
// 上传到R2
await env.MY_BUCKET.put(
`reports/${new Date().toISOString().split('T')[0]}.json`,
JSON.stringify(report)
);
console.log('Maintenance complete');
},
};2. Combined with Hono (Fetch + Scheduled)
2. 与Hono集成(同时支持HTTP请求+定时任务)
Best for: Workers that handle both HTTP requests and scheduled tasks
typescript
// 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));
},
};Why this pattern:
- One Worker handles both use cases
- Share environment bindings
- Reduce number of Workers to manage
- Lower costs (one Worker subscription)
适用场景: 需要同时处理HTTP请求和定时任务的Worker
typescript
// src/index.ts
import { Hono } from 'hono';
interface Env {
DB: D1Database;
}
const app = new Hono<{ Bindings: Env }>();
// 常规HTTP路由
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);
});
// 同时导出fetch处理器和定时处理器
export default {
// 处理HTTP请求
fetch: app.fetch,
// 处理cron触发器
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
console.log('Cron triggered:', controller.cron);
// 运行定时任务
await updateCache(env.DB);
// 记录执行日志
ctx.waitUntil(logExecution(controller.scheduledTime));
},
};模式优势:
- 一个Worker同时支持两种使用场景
- 共享环境变量绑定
- 减少需要管理的Worker数量
- 降低成本(仅需一份Worker订阅)
3. Multiple Cron Triggers
3. 多 Cron 触发器
Best for: Different schedules for different tasks
wrangler.jsonc:
jsonc
{
"triggers": {
"crons": [
"*/5 * * * *", // Every 5 minutes
"0 */6 * * *", // Every 6 hours
"0 0 * * *" // Daily at midnight UTC
]
}
}src/index.ts:
typescript
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}`);
}
},
};CRITICAL:
- Use exact cron expression match (whitespace sensitive)
- Maximum 3 cron triggers per Worker (Free plan)
- Standard/Paid plan supports more (check limits)
适用场景: 不同任务需要不同执行周期
wrangler.jsonc:
jsonc
{
"triggers": {
"crons": [
"*/5 * * * *", // 每5分钟
"0 */6 * * *", // 每6小时
"0 0 * * *" // 每天UTC零点
]
}
}src/index.ts:
typescript
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
// 根据触发的cron表达式路由到不同任务
switch (controller.cron) {
case '*/5 * * * *':
// 每5分钟:检查系统健康状态
await checkSystemHealth(env);
break;
case '0 */6 * * *':
// 每6小时:从外部API同步数据
await syncExternalData(env);
break;
case '0 0 * * *':
// 每天UTC零点:生成报表和清理数据
await generateDailyReports(env);
await cleanupOldData(env);
break;
default:
console.warn(`Unknown cron trigger: ${controller.cron}`);
}
},
};重要提示:
- 使用完全匹配的cron表达式(空格敏感)
- 免费版每个Worker最多支持3个cron触发器
- 标准版/付费版支持更多(参考官方限制说明)
4. Accessing Environment Bindings
4. 访问环境变量绑定
All Worker bindings available in scheduled handler:
typescript
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}` },
});
},
};定时处理器支持所有Worker绑定:
typescript
interface Env {
// 数据库
DB: D1Database;
// 存储
MY_BUCKET: R2Bucket;
KV_NAMESPACE: KVNamespace;
// AI与向量库
AI: Ai;
VECTOR_INDEX: VectorizeIndex;
// 队列与工作流
MY_QUEUE: Queue;
MY_WORKFLOW: Workflow;
// 持久化对象
RATE_LIMITER: DurableObjectNamespace;
// 密钥
API_KEY: string;
}
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
// D1数据库
const users = await env.DB.prepare('SELECT * FROM users WHERE active = 1').all();
// R2存储
const file = await env.MY_BUCKET.get('data.json');
// KV存储
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',
});
// 发送到队列
await env.MY_QUEUE.send({ type: 'process', data: users.results });
// 触发工作流
await env.MY_WORKFLOW.create({ input: { timestamp: Date.now() } });
// 使用密钥
await fetch('https://api.example.com/webhook', {
headers: { Authorization: `Bearer ${env.API_KEY}` },
});
},
};5. Combining with Workflows
5. 与Workflows集成
Best for: Multi-step, long-running tasks triggered on schedule
wrangler.jsonc:
jsonc
{
"triggers": {
"crons": ["0 2 * * *"] // Daily at 2am UTC
},
"workflows": [
{
"name": "daily-report-workflow",
"binding": "DAILY_REPORT"
}
]
}src/index.ts:
typescript
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}`);
},
};Why use Workflows:
- Workflows can run for hours (cron handlers have CPU limits)
- Built-in retry and error handling
- State persistence across steps
- Better for complex, multi-step processes
Reference: Cloudflare Workflows Docs
适用场景: 定时触发的多步骤、长运行时间任务
wrangler.jsonc:
jsonc
{
"triggers": {
"crons": ["0 2 * * *"] // 每天UTC凌晨2点
},
"workflows": [
{
"name": "daily-report-workflow",
"binding": "DAILY_REPORT"
}
]
}src/index.ts:
typescript
interface Env {
DAILY_REPORT: Workflow;
}
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
console.log('Triggering daily report workflow...');
// 传入初始状态触发工作流
const instance = await env.DAILY_REPORT.create({
params: {
date: new Date().toISOString().split('T')[0],
reportType: 'daily-summary',
},
});
console.log(`Workflow started: ${instance.id}`);
},
};使用Workflows的优势:
- Workflows可运行数小时(cron处理器有CPU限制)
- 内置重试和错误处理
- 跨步骤状态持久化
- 更适合复杂的多步骤流程
6. Error Handling in Scheduled Handlers
6. 定时处理器的错误处理
typescript
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}` },
],
},
],
}),
});
}typescript
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void> {
try {
// 主任务
await performScheduledTask(env);
} catch (error) {
// 记录错误
console.error('Scheduled task failed:', error);
// 发送告警
await sendAlert({
worker: 'my-scheduled-worker',
cron: controller.cron,
error: error.message,
timestamp: new Date(controller.scheduledTime).toISOString(),
});
// 将失败记录存入数据库
ctx.waitUntil(
env.DB.prepare(
'INSERT INTO cron_failures (cron, error, timestamp) VALUES (?, ?, ?)'
)
.bind(controller.cron, error.message, Date.now())
.run()
);
// 重新抛出错误,标记执行失败
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 Configuration
Wrangler 配置
Basic Configuration
基础配置
jsonc
{
"name": "my-scheduled-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-23",
"triggers": {
"crons": ["0 * * * *"]
}
}jsonc
{
"name": "my-scheduled-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-23",
"triggers": {
"crons": ["0 * * * *"]
}
}Multiple Cron Triggers
多 Cron 触发器配置
jsonc
{
"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
]
}
}Limits:
- Free: 3 cron schedules max
- Paid: Higher limits (check current limits)
jsonc
{
"triggers": {
"crons": [
"*/5 * * * *", // 每5分钟
"0 */6 * * *", // 每6小时
"0 2 * * *", // 每天UTC凌晨2点
"0 0 * * 1" // 每周一UTC零点
]
}
}限制:
- 免费版:最多3个cron调度规则
- 付费版:更高限制(参考官方最新说明)
Environment-Specific Crons
环境专属 Cron 配置
jsonc
{
"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 specific environment:
bash
undefinedjsonc
{
"name": "my-worker",
"main": "src/index.ts",
"env": {
"dev": {
"triggers": {
"crons": ["*/5 * * * *"] // 开发环境:每5分钟执行,方便测试
}
},
"staging": {
"triggers": {
"crons": ["*/30 * * * *"] // 测试环境:每30分钟执行
}
},
"production": {
"triggers": {
"crons": ["0 * * * *"] // 生产环境:每小时执行
}
}
}
}部署指定环境:
bash
undefinedDeploy to dev
部署到开发环境
npx wrangler deploy --env dev
npx wrangler deploy --env dev
Deploy to production
部署到生产环境
npx wrangler deploy --env production
---npx wrangler deploy --env production
---Removing All Cron Triggers
移除所有 Cron 触发器
jsonc
{
"triggers": {
"crons": [] // Empty array removes all crons
}
}After deploy, Worker will no longer execute on schedule.
jsonc
{
"triggers": {
"crons": [] // 空数组会移除所有cron触发器
}
}部署后,Worker将不再定时执行。
Testing & Development
测试与开发
Local Testing with Wrangler
使用 Wrangler 本地测试
bash
undefinedbash
undefinedStart dev server with scheduled testing enabled
启动开发服务器,开启定时任务测试能力
npx wrangler dev --test-scheduled
This exposes `/__scheduled` endpoint for triggering scheduled handlers.
---npx wrangler dev --test-scheduled
该命令会暴露 `/__scheduled` 端点用于触发定时处理器。
---Trigger Scheduled Handler
触发定时处理器
bash
undefinedbash
undefinedTrigger with default cron (if only one configured)
使用默认cron触发(如果仅配置了一个cron)
Trigger with specific cron expression
使用指定cron表达式触发
Trigger with URL-encoded cron
使用URL编码的cron触发
**Note:** Use `+` instead of spaces in URL, or URL-encode properly.
---
**注意:** URL中请使用`+`代替空格,或者进行正确的URL编码。
---Verify Handler Output
验证处理器输出
bash
undefinedbash
undefinedStart dev server
启动开发服务器
npx wrangler dev --test-scheduled
npx wrangler dev --test-scheduled
In another terminal, trigger and watch output
在另一个终端触发任务,查看输出
Output appears in `wrangler dev` terminal:
[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
---
输出会显示在 `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
---Test Multiple Cron Expressions
测试多个 Cron 表达式
bash
undefinedbash
undefinedTest hourly cron
测试每小时执行的cron
Test daily cron
测试每天执行的cron
Test weekly cron
测试每周执行的cron
Python Workers Testing
Python Worker 测试
bash
undefinedbash
undefinedPython Workers use different endpoint
Python Worker 使用不同的端点
Green Compute
绿色计算
Run cron triggers only in data centers powered by renewable energy.
仅在使用可再生能源供电的数据中心运行 cron 触发器。
Enable Green Compute
开启绿色计算
Via Dashboard:
- Go to Workers & Pages
- In Account details section, find Compute Setting
- Click Change
- Select Green Compute
- Click Confirm
Applies to:
- All cron triggers in your account
- Reduces carbon footprint
- No additional cost
- May introduce slight delays in some regions
How it works:
- Cloudflare routes cron executions to green-powered data centers
- Uses renewable energy: wind, solar, hydroelectric
- Verified through Power Purchase Agreements (PPAs) and Renewable Energy Credits (RECs)
通过控制台开启:
- 打开 Workers & Pages
- 在账户详情区域找到计算设置
- 点击修改
- 选择绿色计算
- 点击确认
生效范围:
- 账户内所有 cron 触发器
- 降低碳足迹
- 无额外费用
- 部分区域可能会有小幅延迟
工作原理:
- Cloudflare 将 cron 执行路由到使用绿色能源供电的数据中心
- 使用的可再生能源包括:风能、太阳能、水能
- 通过购电协议(PPA)和可再生能源证书(REC)验证
Known Issues Prevention
常见问题预防
This skill prevents 6 documented issues:
本教程覆盖了 6 个已知问题的预防方案:
Issue #1: Cron Changes Not Propagating
问题1:Cron 变更未生效
Error: Cron triggers updated in wrangler.jsonc but not executing
Source: Cloudflare Docs - Cron Triggers
Why It Happens:
- Changes to cron triggers take up to 15 minutes to propagate globally
- Cloudflare network needs time to update edge nodes
- No instant propagation like regular deploys
Prevention:
- Wait 15 minutes after deploy before expecting execution
- Check dashboard: Workers & Pages > [Worker] > Cron Triggers
- Use for trigger-only changes
wrangler triggers deploy
bash
undefined错误表现: 修改了wrangler.jsonc中的cron触发器,但任务未执行
原因:
- Cron 触发器变更最多需要 15分钟 完成全球节点同步
- Cloudflare 网络需要时间更新边缘节点配置
- 不像常规代码部署可以即时生效
预防方案:
- 部署后等待15分钟再验证执行情况
- 在控制台查看配置:Workers & Pages > [你的Worker] > Cron Triggers
- 如果仅修改了触发器配置,使用 部署
wrangler triggers deploy
bash
undefinedIf you only changed triggers (not code), use:
如果你仅修改了触发器(没有修改代码),使用:
npx wrangler triggers deploy
npx wrangler triggers deploy
Wait 15 minutes, then verify in dashboard
等待15分钟后,在控制台验证配置
---
---Issue #2: Handler Does Not Export
问题2:处理器未导出
Error:
Handler does not export a 'scheduled' methodSource: Common deployment error
Why It Happens:
- Handler not named exactly
scheduled - Handler not exported in default export object
- Using Service Worker format instead of ES modules
Prevention:
typescript
// ❌ 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) { }
};错误信息:
Handler does not export a 'scheduled' method来源: 常见部署错误
原因:
- 处理器名称不是精确的
scheduled - 处理器没有在默认导出对象中导出
- 使用了Service Worker格式而不是ES modules
预防方案:
typescript
// ❌ 错误:处理器名称不正确
export default {
async scheduledHandler(controller, env, ctx) { }
};
// ❌ 错误:没有放在默认导出中
export async function scheduled(controller, env, ctx) { }
// ✅ 正确:默认导出中名为'scheduled'的处理器
export default {
async scheduled(controller, env, ctx) { }
};Issue #3: UTC Timezone Confusion
问题3:UTC时区混淆
Error: Cron runs at wrong time
Source: User expectation vs. reality
Why It Happens:
- All cron triggers run on UTC time only
- No timezone conversion available
- Users expect local timezone
Prevention:
Convert your local time to UTC manually:
typescript
// 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-7Tools:
错误表现: Cron执行时间和预期不符
来源: 用户预期与实际实现的差异
原因:
- 所有 cron 触发器仅按UTC时间执行
- 无内置时区转换能力
- 用户默认按本地时区配置
预防方案:
手动将本地时间转换为UTC时间:
typescript
// 想要在太平洋标准时间上午9点执行(UTC-8)?
// 太平洋标准时间上午9点 = UTC下午5点(17:00)
{
"triggers": {
"crons": ["0 17 * * *"] // 太平洋标准时间上午9点 = UTC下午5点
}
}
// 想要在东部标准时间下午6点执行(UTC-5)?
// 东部标准时间下午6点 = UTC晚上11点(23:00)
{
"triggers": {
"crons": ["0 23 * * *"] // 东部标准时间下午6点 = UTC晚上11点
}
}
// 注意:夏令时变化会影响转换结果!
// 太平洋标准时间是UTC-8,太平洋夏令时是UTC-7工具:
Issue #4: Invalid Cron Expression
问题4:无效的 Cron 表达式
Error: Cron doesn't execute, no error shown
Source: Silent validation failure
Why It Happens:
- Invalid cron syntax silently fails
- Validation happens at deploy, but may not be obvious
- Common mistakes: wrong field order, invalid ranges
Prevention:
bash
undefined错误表现: Cron未执行,也没有错误提示
来源: 静默验证失败
原因:
- 无效的cron语法会静默失败
- 部署时会进行验证,但错误提示不明显
- 常见错误:字段顺序错误、值范围无效
预防方案:
bash
undefined❌ Wrong: Too many fields (6 fields instead of 5)
❌ 错误:字段过多(6个字段而不是5个)
"crons": ["0 0 * * * *"] # Has seconds field - not supported
"crons": ["0 0 * * * *"] # 包含秒字段 - 不支持
❌ Wrong: Invalid minute range
❌ 错误:分钟范围无效
"crons": ["65 * * * *"] # Minute must be 0-59
"crons": ["65 * * * *"] # 分钟范围必须是0-59
❌ Wrong: Invalid day of week
❌ 错误:星期值无效
"crons": ["0 0 * * 7"] # Day of week is 0-6 (use 0 for Sunday)
"crons": ["0 0 * * 7"] # 星期范围是0-6(周日用0表示)
✅ Correct: 5 fields, valid ranges
✅ 正确:5个字段,值范围有效
"crons": ["0 0 * * 0"] # Sunday at midnight UTC
**Validation:**
- Use [Crontab Guru](https://crontab.guru/) to validate expressions
- Check wrangler deploy output for errors
- Test locally with `--test-scheduled`
---"crons": ["0 0 * * 0"] # 周日UTC零点执行
**验证方法:**
- 使用 [Crontab Guru](https://crontab.guru/) 验证表达式
- 检查 wrangler deploy 输出的错误信息
- 使用 `--test-scheduled` 本地测试
---Issue #5: Missing ES Modules Format
问题5:未使用 ES Modules 格式
Error:
Worker must use ES modules formatSource: Legacy Service Worker format
Why It Happens:
- Scheduled handler requires ES modules format
- Old Service Worker format not supported
- Mixed format in codebase
Prevention:
typescript
// ❌ 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);
},
};错误信息:
Worker must use ES modules format来源: 旧版Service Worker格式
原因:
- 定时处理器要求使用ES modules格式
- 不支持旧的Service Worker格式
- 代码库混合使用两种格式
预防方案:
typescript
// ❌ 错误:Service Worker格式
addEventListener('scheduled', (event) => {
event.waitUntil(handleScheduled(event));
});
// ✅ 正确:ES modules格式
export default {
async scheduled(controller, env, ctx) {
await handleScheduled(controller, env, ctx);
},
};Issue #6: CPU Time Limits Exceeded
问题6:CPU时间超限
Error:
CPU time limit exceededSource: Long-running scheduled tasks
Why It Happens:
- Default CPU limit: 30 seconds
- Long-running tasks exceed limit
- No automatic timeout extension
Prevention:
Option 1: Increase CPU limit in wrangler.jsonc
jsonc
{
"limits": {
"cpu_ms": 300000 // 5 minutes (max for Standard plan)
}
}Option 2: Use Workflows for long-running tasks
typescript
// 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' },
});
},
};Option 3: Break into smaller chunks
typescript
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' });
}
},
};错误信息:
CPU time limit exceeded来源: 运行时间过长的定时任务
原因:
- 默认CPU限制:30秒
- 运行时间过长的任务超出限制
- 没有自动超时延长机制
预防方案:
方案1:在wrangler.jsonc中提高CPU限制
jsonc
{
"limits": {
"cpu_ms": 300000 // 5分钟(标准版计划的最大值)
}
}方案2:长运行任务使用Workflows
typescript
// 不要在cron中运行长任务:
export default {
async scheduled(controller, env, ctx) {
// 触发可以运行数小时的Workflow
await env.MY_WORKFLOW.create({
params: { task: 'long-running-job' },
});
},
};方案3:拆分为更小的任务块
typescript
export default {
async scheduled(controller, env, ctx) {
// 批量处理
const batch = await getNextBatch(env.DB);
for (const item of batch) {
await processItem(item);
}
// 如果还有未处理的任务,发送到队列处理下一批
const hasMore = await hasMoreWork(env.DB);
if (hasMore) {
await env.MY_QUEUE.send({ type: 'continue-processing' });
}
},
};Always Do ✅
最佳实践 ✅
- Use exact handler name - Must be , not
scheduledor variantsscheduledHandler - Use ES modules format - Export in default object, not addEventListener
- Convert to UTC - All cron times are UTC, convert from local timezone
- Wait 15 minutes - Cron changes take up to 15 min to propagate
- Test locally first - Use
wrangler dev --test-scheduled - Validate cron syntax - Use Crontab Guru
- Handle errors gracefully - Log, alert, and optionally re-throw
- Use ctx.waitUntil() - For non-critical async operations
- Consider Workflows - For tasks that need >30 seconds CPU time
- Monitor executions - Check dashboard logs regularly
- 使用精确的处理器名称 - 必须是 ,不能是
scheduled或其他变体scheduledHandler - 使用ES modules格式 - 在默认导出对象中导出,不要用addEventListener
- 转换为UTC时间 - 所有cron时间都是UTC,从本地时区转换后再配置
- 等待15分钟生效 - Cron变更最多需要15分钟完成全球同步
- 优先本地测试 - 使用 测试
wrangler dev --test-scheduled - 验证Cron语法 - 使用 Crontab Guru 校验
- 优雅处理错误 - 记录日志、发送告警,可选择重新抛出错误
- 合理使用ctx.waitUntil() - 用于非关键异步操作
- 长任务考虑使用Workflows - 对于需要超过30秒CPU时间的任务
- 监控执行情况 - 定期查看控制台日志
Never Do ❌
禁止操作 ❌
- Never assume local timezone - All crons run on UTC
- Never use 6-field cron expressions - Cloudflare uses 5-field format (no seconds)
- Never rely on instant propagation - Changes take up to 15 minutes
- Never use Service Worker format - Must use ES modules format
- Never forget error handling - Uncaught errors fail silently
- Never run CPU-intensive tasks without limit increase - Default 30s limit
- Never use day-of-week 7 - Use 0 for Sunday (0-6 range only)
- Never deploy without testing - Always test with first
--test-scheduled - Never ignore execution logs - Dashboard shows past failures
- Never hardcode schedules for testing - Use environment-specific configs
- 不要假设使用本地时区 - 所有cron都按UTC时间执行
- 不要使用6字段的cron表达式 - Cloudflare使用5字段格式(无秒字段)
- 不要依赖即时生效 - 变更最多需要15分钟同步
- 不要使用Service Worker格式 - 必须使用ES modules格式
- 不要遗漏错误处理 - 未捕获的错误会静默失败
- 不要在未提高限制的情况下运行CPU密集型任务 - 默认只有30秒限制
- 不要使用7作为星期值 - 周日用0表示(仅支持0-6范围)
- 不要未经测试就部署 - 总是先使用 本地测试
--test-scheduled - 不要忽略执行日志 - 控制台会显示历史失败记录
- 不要硬编码测试用的调度规则 - 使用环境专属配置
Common Use Cases
常见使用场景
1. Database Cleanup
1. 数据库清理
Every day at 2am UTC: Delete old records
typescript
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:
jsonc
{
"triggers": {
"crons": ["0 2 * * *"] // Daily at 2am UTC
}
}每天UTC凌晨2点:删除旧记录
typescript
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
// 删除30天前的会话
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
await env.DB.prepare('DELETE FROM sessions WHERE created_at < ?')
.bind(thirtyDaysAgo)
.run();
// 删除90天前软删除的用户
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:
jsonc
{
"triggers": {
"crons": ["0 2 * * *"] // 每天UTC凌晨2点
}
}2. API Data Collection
2. API数据采集
Every 15 minutes: Fetch data from external API
typescript
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:
jsonc
{
"triggers": {
"crons": ["*/15 * * * *"] // Every 15 minutes
}
}每15分钟:从外部API拉取数据
typescript
interface Env {
DB: D1Database;
API_KEY: string;
}
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
try {
// 从外部API拉取数据
const response = await fetch('https://api.example.com/v1/data', {
headers: {
Authorization: `Bearer ${env.API_KEY}`,
},
});
const data = await response.json();
// 存入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; // 标记执行失败
}
},
};wrangler.jsonc:
jsonc
{
"triggers": {
"crons": ["*/15 * * * *"] // 每15分钟
}
}3. Daily Reports Generation
3. 每日报表生成
Every day at 8am UTC: Generate and email report
typescript
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:
jsonc
{
"triggers": {
"crons": ["0 8 * * *"] // Daily at 8am UTC
}
}每天UTC上午8点:生成并发送报表邮件
typescript
export default {
async scheduled(controller: ScheduledController, env: Env, ctx: ExecutionContext): Promise<void> {
// 从数据库生成报表
const report = await generateDailyReport(env.DB);
// 存入R2
const fileName = `reports/${new Date().toISOString().split('T')[0]}.json`;
await env.MY_BUCKET.put(fileName, JSON.stringify(report));
// 发送邮件
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:
jsonc
{
"triggers": {
"crons": ["0 8 * * *"] // 每天UTC上午8点
}
}4. Cache Warming
4. 缓存预热
Every hour: Pre-warm cache with popular content
typescript
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:
jsonc
{
"triggers": {
"crons": ["0 * * * *"] // Every hour
}
}每小时:预加载热门内容到缓存
typescript
export default {
async scheduled(controller: ScheduledController, env: Env): Promise<void> {
// 从统计数据获取热门页面
const popularPages = await env.DB
.prepare('SELECT url FROM pages ORDER BY views DESC LIMIT 100')
.all();
// 请求每个页面预热缓存
const requests = popularPages.results.map((page) =>
fetch(`https://example.com${page.url}`, {
cf: {
cacheTtl: 3600, // 缓存1小时
},
})
);
await Promise.all(requests);
console.log(`Warmed cache for ${popularPages.results.length} pages`);
},
};wrangler.jsonc:
jsonc
{
"triggers": {
"crons": ["0 * * * *"] // 每小时
}
}5. Monitoring & Health Checks
5. 监控与健康检查
Every 5 minutes: Check system health
typescript
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:
jsonc
{
"triggers": {
"crons": ["*/5 * * * *"] // Every 5 minutes
}
}每5分钟:检查系统健康状态
typescript
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) {
// 发送告警
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:
jsonc
{
"triggers": {
"crons": ["*/5 * * * *"] // 每5分钟
}
}TypeScript Types
TypeScript 类型定义
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>;
}typescript
// 定时事件控制器
interface ScheduledController {
readonly cron: string;
readonly type: string;
readonly scheduledTime: number;
}
// 执行上下文
interface ExecutionContext {
waitUntil(promise: Promise<any>): void;
passThroughOnException(): void;
}
// 定时处理器
export default {
async scheduled(
controller: ScheduledController,
env: Env,
ctx: ExecutionContext
): Promise<void>;
}Limits & Pricing
限制与定价
Limits
使用限制
| Feature | Free Plan | Paid Plan |
|---|---|---|
| Cron triggers per Worker | 3 | Higher (check docs) |
| CPU time per execution | 10 ms (avg) | 30 seconds (default), 5 min (max) |
| Wall clock time | 30 seconds | 15 minutes |
| Memory | 128 MB | 128 MB |
| 功能 | 免费计划 | 付费计划 |
|---|---|---|
| 每个Worker的Cron触发器数量 | 3 | 更高(参考官方文档) |
| 每次执行CPU时间 | 平均10毫秒 | 默认30秒,最高5分钟 |
| 运行时长 | 30秒 | 15分钟 |
| 内存 | 128 MB | 128 MB |
Pricing
定价
Cron triggers use Standard Workers pricing:
- Workers Paid Plan: $5/month required
- Requests: $0.30 per million requests (after 10M free)
- CPU Time: $0.02 per million CPU-ms (after 30M free)
Cron execution = 1 request
Example:
- Cron runs every hour (24 times/day)
- 30 days × 24 executions = 720 executions/month
- Average 50ms CPU time per execution
Cost:
- Requests: 720 (well under 10M free)
- CPU time: 720 × 50ms = 36,000ms (under 30M free)
- Total: $5/month (just subscription)
High frequency example:
- Cron runs every minute (1440 times/day)
- 30 days × 1440 = 43,200 executions/month
- Still under free tier limits
- Total: $5/month
Cron触发器按标准Worker定价计费:
- Worker付费计划:每月最低5美元
- 请求数:每月前1000万次请求免费,超出后每百万次0.3美元
- CPU时间:每月前3000万CPU毫秒免费,超出后每百万CPU毫秒0.02美元
每次Cron执行计为1次请求
示例:
- Cron每小时执行1次(每天24次)
- 30天 × 24次 = 每月720次执行
- 每次执行平均CPU耗时50毫秒
费用:
- 请求数:720次(远低于1000万免费额度)
- CPU时间:720 × 50毫秒 = 36000毫秒(低于3000万免费额度)
- 总计:每月5美元(仅订阅费用)
高频执行示例:
- Cron每分钟执行1次(每天1440次)
- 30天 × 1440次 = 每月43200次执行
- 仍低于免费额度限制
- 总计:每月5美元
Troubleshooting
故障排查
Issue: Cron not executing
问题:Cron没有执行
Possible causes:
- Changes not propagated yet (wait 15 minutes)
- Invalid cron expression
- Handler not exported correctly
- Worker not deployed
Solution:
bash
undefined可能原因:
- 变更还未完成同步(等待15分钟)
- Cron表达式无效
- 处理器导出不正确
- Worker未部署
解决方案:
bash
undefinedRe-deploy
重新部署
npx wrangler deploy
npx wrangler deploy
Wait 15 minutes
等待15分钟
Check dashboard
检查控制台配置
Workers & Pages > [Worker] > Cron Triggers
Workers & Pages > [你的Worker] > Cron Triggers
Check logs
查看日志
Workers & Pages > [Worker] > Logs > Real-time Logs
Workers & Pages > [你的Worker] > 日志 > 实时日志
---
---Issue: Handler executes but fails
问题:处理器触发但执行失败
Possible causes:
- Uncaught error in handler
- CPU time limit exceeded
- Missing environment bindings
- Network timeout
Solution:
typescript
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;
}
},
};Check logs in dashboard for error details.
可能原因:
- 处理器中有未捕获的错误
- CPU时间超限
- 缺少环境变量绑定
- 网络超时
解决方案:
typescript
export default {
async scheduled(controller, env, ctx) {
try {
await yourTask(env);
} catch (error) {
// 记录详细错误信息
console.error('Handler failed:', {
error: error.message,
stack: error.stack,
cron: controller.cron,
time: new Date(controller.scheduledTime),
});
// 发送告警
ctx.waitUntil(sendAlert(error));
// 重新抛出错误标记执行失败
throw error;
}
},
};在控制台日志中查看详细错误信息。
Issue: Wrong execution time
问题:执行时间不对
Cause: UTC vs. local timezone confusion
Solution:
Convert your desired local time to UTC:
typescript
// Want 9am PST (UTC-8)?
// 9am PST = 5pm UTC (17:00)
{
"triggers": {
"crons": ["0 17 * * *"]
}
}Tools:
- World Clock Converter
- Remember DST changes (PST vs PDT)
原因: UTC和本地时区混淆
解决方案:
将你需要的本地时间手动转换为UTC时间:
typescript
// 想要在太平洋标准时间上午9点执行(UTC-8)?
// 太平洋标准时间上午9点 = UTC下午5点(17:00)
{
"triggers": {
"crons": ["0 17 * * *"]
}
}工具:
- 世界时间转换器
- 注意夏令时变化(PST和PDT的区别)
Issue: Local testing not working
问题:本地测试不生效
Possible causes:
- Missing flag
--test-scheduled - Wrong endpoint (should be )
/__scheduled - Python Worker (use )
/cdn-cgi/handler/scheduled
Solution:
bash
undefined可能原因:
- 缺少 标志
--test-scheduled - 端点错误(应该是 )
/__scheduled - 使用Python Worker(请用 )
/cdn-cgi/handler/scheduled
解决方案:
bash
undefinedCorrect: Start with flag
正确:启动时添加标志
npx wrangler dev --test-scheduled
npx wrangler dev --test-scheduled
In another terminal
在另一个终端触发
---
---Production Checklist
生产环境检查清单
Before deploying cron triggers to production:
- Cron expression validated on Crontab Guru
- Handler named exactly in default export
scheduled - ES modules format used (not Service Worker)
- Local timezone converted to UTC
- Error handling implemented with logging
- Alerts configured for failures
- CPU limits increased if needed ()
limits.cpu_ms - Environment bindings tested
- Tested locally with
--test-scheduled - Deployment tested in staging environment
- Waited 15 minutes after deploy for propagation
- Verified execution in dashboard logs
- Monitoring and alerting configured
- Documentation updated with schedule details
将Cron触发器部署到生产环境前,请确认:
- Cron表达式已在 Crontab Guru 验证通过
- 处理器在默认导出中命名为精确的
scheduled - 使用了ES modules格式(不是Service Worker)
- 本地时间已转换为UTC时间
- 已实现错误处理和日志记录
- 已配置失败告警
- 如有需要已提高CPU限制()
limits.cpu_ms - 环境变量绑定已测试
- 已使用 本地测试通过
--test-scheduled - 已在测试环境部署验证
- 部署后已等待15分钟完成同步
- 已在控制台日志验证执行成功
- 已配置监控和告警
- 已更新文档记录调度规则
Related Documentation
相关文档
- Cloudflare Cron Triggers: https://developers.cloudflare.com/workers/configuration/cron-triggers/
- Scheduled Handler API: https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/
- Cron Trigger Examples: https://developers.cloudflare.com/workers/examples/cron-trigger/
- Multiple Cron Triggers: https://developers.cloudflare.com/workers/examples/multiple-cron-triggers/
- Wrangler Triggers Command: https://developers.cloudflare.com/workers/wrangler/commands/#triggers
- Workers Pricing: https://developers.cloudflare.com/workers/platform/pricing/
- Workflows Integration: https://developers.cloudflare.com/workflows/
- Crontab Guru (validator): https://crontab.guru/
- Time Zone Converter: https://www.timeanddate.com/worldclock/converter.html
Last Updated: 2025-10-23
Version: 1.0.0
Maintainer: Jeremy Dawes | jeremy@jezweb.net
- Cloudflare Cron Triggers 官方文档: 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 Triggers 命令: https://developers.cloudflare.com/workers/wrangler/commands/#triggers
- Worker定价: 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 | jeremy@jezweb.net