ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

如何用 Payload Jobs Queue 配置 schedule 实现 cron 定时任务

2026/9/10 13:51:41 拓冰建站 浏览量
如何用 Payload Jobs Queue 配置 schedule 实现 cron 定时任务 如何用 Payload Jobs Queue 配置 schedule 实现 cron 定时任务【免费下载链接】payloadPayload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for building powerful applications.项目地址: https://gitcode.com/GitHub_Trending/pa/payload当你需要让 Payload 里的任务Task或工作流Workflow按 cron 表达式周期性地自动执行——比如每天早 9 点生成周报、每小时同步一次外部数据——可以把schedule属性加到 task 或 workflow 配置上。Payload 会按 cron 把 Job 写入数据库队列再由你配置的 runner 把到期的 Job 取出来执行。本文的适用前提是项目中已启用 Payload 的 Jobs Queue 功能在payload.config.ts中配置jobs部署环境可以是常驻服务器dedicated server或 serverless 平台两者配置路径略有差异。要特别强调的一点schedule本身只负责入队不会执行任何业务逻辑。官方文档明确指出定时任务需要两套配置同时生效只配一套不会工作Schedule 配置在 task/workflow 上定义schedule负责按 cron 把 Job 放入队列QueuingRunner 配置bin script、autoRun或 API endpoint负责执行队列里的 JobRunning并且默认同时触发 schedule 的调度逻辑。两者使用的queue名称必须一致Job 才能从调度流转到执行。第 1 步在 Payload config 中定义带 schedule 的 Taskschedule是ScheduleConfig数组每一项包含cron必填支持秒级精度、queue必填Job 要放入的队列名和可选的hooksbeforeSchedule/afterSchedule用于定制调度行为。以下是一个每天早上 7 点发摘要邮件的示例handler 内部的业务逻辑请替换为你自己的代码示例取自 Job Schedules 文档export const DailyDigestTask: TaskConfigDailyDigest { slug: DailyDigest, schedule: [ { cron: 0 7 * * *, // Every day at 7:00 AM queue: emails, }, ], handler: async ({ req }) { // 你的业务逻辑例如查询用户并发邮件 return { output: { emailsSent: 0 } } }, }然后把 task 注册到jobs.tasks中参见 Tasks 文档。如果只需要在未来某个一次性时间点执行任务不要用schedule——文档建议改用payload.jobs.queue()的waitUntil参数await payload.jobs.queue({ task: publishPost, input: { postId: 123 }, waitUntil: new Date(2024-12-25T15:00:00Z), // Runs once at this specific time })schedule的调度生命周期大致是Payload 读取payload-jobs-stats这个 global 中每个 scheduled task 的上次调度时间 → 执行默认的beforeSchedulehook若已存在同类型的 active/runnable 定时 Job 则跳过防止重复入队→ 把新 Job 入队其waitUntil设为下一个 cron 时间点→ 默认afterSchedulehook 更新payload-jobs-stats中的 lastScheduled 时间。第 2 步选择一种 runner 配置同时完成调度触发与执行文档给出了四种执行方式按部署环境选择其一即可。注意不要对同一个队列同时启用 bin script 的handle-schedules和autoRun否则两边都会调用调度逻辑导致重复入队。方式 ABin script推荐用于专属服务器bin script 在独立于 Next.js 服务器的进程中运行方便单独部署、监控和扩展 worker。一条命令同时完成调度和执行# 按 cron 触发调度并从队列中执行到期 Job pnpm payload jobs:run --cron */5 * * * * --queue emails --handle-schedules如果希望调度和执行分离成两个进程可以只跑调度再单独起执行进程# 只处理调度调度出的 Job 仍需另一个进程去 run pnpm payload jobs:handle-schedules --cron */5 * * * * --queue emails # 或处理所有队列 pnpm payload jobs:handle-schedules --cron */5 * * * * --all-queuesbin script 的优势与 Next.js 进程隔离、可作为独立服务/容器部署、多实例横向扩展时不影响 API 性能。Queues 文档还给出了 docker-compose 示例把worker-nightly定义为pnpm payload jobs:run --cron * * * * * --queue nightly --handle-schedules与主应用的pnpm start并列部署。方式 BautoRun专属服务器的替代方案autoRun在 Next.js 进程内以 cron 方式执行队列里的 Job并且默认同时处理调度disableScheduling默认为falseexport default buildConfig({ jobs: { tasks: [DailyDigestTask], autoRun: [ { cron: * * * * *, // Runs every minute queue: emails, }, ], }, })两个注意事项autoRun要求服务器常驻运行不要用在 Vercel 等 serverless 平台文档明确要求如果你用 bin script 单独处理调度、只想让autoRun执行 Job要设置disableScheduling: true避免重复入队。shouldAutoRun函数可以用来控制是否执行 Job例如让多个实例中只有一个处理调度jobs: { shouldAutoRun: () process.env.HANDLE_SCHEDULES true, autoRun: [/* ... */], }方式 CAPI endpointserverless 平台serverless 环境下 Payload 不会自动跑 cron 调度需要由外部定时系统如 Vercel Cron周期性触发内置的 endpoint。GET /api/payload-jobs/run默认同时处理调度也可以只触发调度GET /api/payload-jobs/handle-schedules?queueemails。Vercel 上在vercel.json中配置 Cron{ crons: [ { path: /api/payload-jobs/run, schedule: */5 * * * * } ] }并设置环境变量CRON_SECRET建议 16 位以上随机字符串通过jobs.access.run限制只有登录用户或携带该 secret 的请求才能触发 runnerexport default buildConfig({ jobs: { access: { run: ({ req }: { req: PayloadRequest }): boolean { // Allow logged in users to execute this endpoint (default) if (req.user) return true const secret process.env.CRON_SECRET if (!secret) return false const authHeader req.headers.get(authorization) return authHeader Bearer ${secret} }, }, }, })endpoint 的 query 参数来自 Queues 文档limit本次最多执行多少 Job默认 10、queue不指定时为default队列、allQueues为true时忽略queue参数跑所有队列。cron 表达式格式与常用写法schedules文档给出的格式参考秒字段可选0-59* * * * * * │ │ │ │ │ │ │ │ │ │ │ └─ Day of week (0-7, 0 and 7 Sunday) │ │ │ │ └─── Month (1-12) │ │ │ └───── Day of month (1-31) │ │ └─────── Hour (0-23) │ └───────── Minute (0-59) └─────────── Second (0-59, optional)常用模式摘自 Job Schedules 文档// Every minute schedule: [{ cron: * * * * *, queue: frequent }] // Every 5 minutes schedule: [{ cron: */5 * * * *, queue: default }] // Every hour at minute 0 schedule: [{ cron: 0 * * * *, queue: hourly }] // Every day at midnight (00:00) schedule: [{ cron: 0 0 * * *, queue: nightly }] // Every Monday at 9:00 AM schedule: [{ cron: 0 9 * * 1, queue: weekly }] // First day of every month at midnight schedule: [{ cron: 0 0 1 * *, queue: monthly }] // Every weekday (Mon-Fri) at 8:00 AM schedule: [{ cron: 0 8 * * 1-5, queue: weekdays }] // Every 30 seconds (with seconds precision) schedule: [{ cron: */30 * * * * *, queue: frequent }]验证定时任务是否按预期入队与执行检查是否已入队——查询内部的payload-jobscollection确认存在completedAt为 null 的 Job并看它的waitUntil是否指向下一个 cron 时间点这是正常现象Job 只会在waitUntil之后才被 runner 执行const jobs await payload.find({ collection: payload-jobs, where: { completedAt: { exists: false }, }, }) console.log(jobs.docs[0].waitUntil) // Check if this is in the future检查调度是否发生过——读取payload-jobs-statsglobal 的lastScheduled确认每个 task 的上次调度时间const stats await payload.findGlobal({ slug: payload-jobs-stats, }) console.log(stats.lastScheduled) // Check when each task was last scheduled检查执行结果——Job 执行后通过payload-jobscollection 的字段判断状态processingUntil在未来表示当前有 worker 持有该 JobhasError: true时查看error与log字段completedAt为空表示还没跑过const job await payload.findByID({ collection: payload-jobs, id: jobId, }) console.log(job.log) // View execution log console.log(job.processingErrors) // View errors注意payload-jobscollection 默认对 Admin Panel 隐藏、拒绝普通访问因为 Job 数据可能含敏感的输入输出与日志。如需在面板中只读查看可用jobs.jobsCollectionOverrides覆盖为只读访问见 Overview 文档的 Inspecting Jobs 一节。常见问题排查按 schedules 文档 和 queues 文档 的 Troubleshooting 章节Job 完全没有入队确认autoRun没有设disableScheduling: true如果你依赖 autoRun 做调度的话确认 cron 表达式合法5 字段是标准格式6 字段带秒字段数量对不上是典型错误确认 task 确实有schedule属性且存在某个机制在触发调度bin script、autoRun 或 endpoint——只定义schedule而没有任何调度触发机制是最常见的坑文档列为 Defining schedules without executing them。Job 已入队但没执行队列名不匹配schedule里的queue与 runnerautoRun/--queue参数里的queue必须完全一致serverless 平台上用了autoRunautoRun需要常驻服务器请改用 endpoint 外部 cron开发环境热更新HMR会打断 cron 调度这是 Next.js 开发模式的预期行为修改 Job 配置后重启 dev server 即可生产环境不受影响。同一任务出现多个实例重复入队多个服务器实例都在处理调度时可能各自入队一次。解法是只在一个实例上启用调度如用HANDLE_SCHEDULES环境变量区分其余实例设disableScheduling: true或者你对同一队列同时启用了handle-schedulesbin script 和autoRun默认会调度二选一即可。另外如果你自定义了beforeSchedulehook要确保它正确检查了已存在的 Job否则默认的去重行为会被你的 hook 覆盖import { countRunnableOrActiveJobsForQueue } from payload hooks: { beforeSchedule: async ({ queueable, req }) { const count await countRunnableOrActiveJobsForQueue({ queue: queueable.scheduleConfig.queue, req, taskSlug: queueable.taskConfig?.slug, onlyScheduled: true, }) return { shouldSchedule: count 0, // Only schedule if no jobs exist } }, }限制与边界schedule只负责入队一次性定时任务请改用waitUntilautoRun仅适用于常驻服务器serverless 平台一律用 endpoint 方式租约processing lease默认时长 20 分钟、安全缓冲 30 秒可通过jobs.processingLease调整lease 过期不会自动拉起新进程serverless 部署仍需要外部 cron 持续调用 runner。完成上述配置后验证路径就是到 cron 时间点附近查询payload-jobs-stats确认lastScheduled已更新、payload-jobscollection 中出现新 Job随后该 Job 被 runner 执行并写入completedAt与output。若某一步不成立按上一节的排查项逐项核对即可。更完整的队列策略优先级队列、按功能分队列、processingOrder等可继续参考 Queues 文档。【免费下载链接】payloadPayload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for building powerful applications.项目地址: https://gitcode.com/GitHub_Trending/pa/payload创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考