使用 PM2 部署 Node.js 应用时,内存中的异步定时任务会在进程重启后丢失,最可靠的配置方案是将定时任务剥离到独立 Worker 进程或使用外部调度器触发 API 接口。
先说结论:PM2 管理的是进程生命周期,无法持久化应用内存中的定时器状态,必须通过架构调整解决。
- 适合:关键业务定时任务、需要断点续跑的场景
- 先准备:独立 Worker 脚本、数据库状态记录表
- 验收:进程重启后任务能自动补跑或跳过
命令速用版
通过 PM2 ecosystem 配置文件将定时任务拆分为独立进程,避免随 Web 服务重启而丢失。
module.exports = {
apps: [{
name: 'web-app',
script: './src/server.js',
instances: 4,
exec_mode: 'cluster'
}, {
name: 'cron-worker',
script: './src/worker.js',
instances: 1,
exec_mode: 'fork',
cron_restart: '0 */1 * * * *'
}]
}使用命令 pm2 start ecosystem.config.js 启动,确保 cron-worker 实例数为 1 且模式为 fork。
为什么会这样
进程重启意味着内存变量清零,JavaScript 单线程特性导致定时器绑定在当前事件循环中。PM2 重启进程时会杀死旧进程并启动新进程,旧进程中的 setInterval 或 node-cron 实例随之销毁,且没有机制通知新进程上次执行到哪里。
分步处理
第一步是拆分任务进程,将定时逻辑从 Web 服务器代码中移除,写入独立的 worker.js 文件,并在 PM2 配置中单独声明。
第二步是持久化任务状态,在数据库中建立任务执行记录表,每次任务开始前查询上次执行时间,结束后更新当前时间,确保重启后可判断是否需要补跑。
第三步是增加分布式锁,如果 PM2 配置了多实例,必须使用 Redis 锁确保同一时间只有一个进程执行任务,防止重复消费。
怎么验证是否生效
查看 PM2 日志确认 Worker 进程独立运行,使用命令 pm2 logs cron-worker 观察重启前后日志是否连续。
检查数据库任务记录表,确认进程重启后,新进程能读取到上次执行时间戳,并根据业务逻辑决定是否执行遗漏的任务。
常见坑
PM2 的 instances 参数如果大于 1,会导致定时任务重复执行,必须将定时任务进程的实例数强制设为 1。
服务器时区配置可能与代码中 cron 表达式不一致,导致任务执行时间偏移,建议在代码中统一使用 UTC 时间或明确指定时区。
不要依赖 PM2 的 cron_restart 来执行业务逻辑,该参数仅用于定时重启进程,而非定时触发任务函数。
常见问题
PM2 的 cron_restart 能代替定时任务吗?
不能,cron_restart 仅用于定时重启进程,无法触发应用内的具体业务函数。
多实例模式下如何防止任务重复?
将定时任务进程 instances 设为 1,或使用 Redis 锁机制确保同一时间只有一个实例执行。
进程崩溃后任务会立即补跑吗?
不会自动补跑,需要在代码中读取数据库最后执行时间,判断时间差后手动触发补跑逻辑。
参考来源
- PM2 Official Documentation, Process Management, https://pm2.io/docs/runtime/guide/process-management/
- node-cron GitHub Repository, Usage Examples, https://github.com/kelektiv/node-cron