使用 PM2 部署 Node.js 应用时异步定时任务进程重启丢失怎么配置?

文章导读
使用 PM2 部署 Node.js 应用时,内存中的异步定时任务会在进程重启后丢失,最可靠的配置方案是将定时任务剥离到独立 Worker 进程或使用外部调度器触发 API 接口。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 常见问题
  7. 参考来源
A A

使用 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 重启进程时会杀死旧进程并启动新进程,旧进程中的 setIntervalnode-cron 实例随之销毁,且没有机制通知新进程上次执行到哪里。

使用 PM2 部署 Node.js 应用时异步定时任务进程重启丢失怎么配置?

分步处理

第一步是拆分任务进程,将定时逻辑从 Web 服务器代码中移除,写入独立的 worker.js 文件,并在 PM2 配置中单独声明。

第二步是持久化任务状态,在数据库中建立任务执行记录表,每次任务开始前查询上次执行时间,结束后更新当前时间,确保重启后可判断是否需要补跑。

第三步是增加分布式锁,如果 PM2 配置了多实例,必须使用 Redis 锁确保同一时间只有一个进程执行任务,防止重复消费。

怎么验证是否生效

查看 PM2 日志确认 Worker 进程独立运行,使用命令 pm2 logs cron-worker 观察重启前后日志是否连续。

使用 PM2 部署 Node.js 应用时异步定时任务进程重启丢失怎么配置?

检查数据库任务记录表,确认进程重启后,新进程能读取到上次执行时间戳,并根据业务逻辑决定是否执行遗漏的任务。

常见坑

PM2 的 instances 参数如果大于 1,会导致定时任务重复执行,必须将定时任务进程的实例数强制设为 1。

服务器时区配置可能与代码中 cron 表达式不一致,导致任务执行时间偏移,建议在代码中统一使用 UTC 时间或明确指定时区。

使用 PM2 部署 Node.js 应用时异步定时任务进程重启丢失怎么配置?

不要依赖 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