你的浏览器无法正常显示内容,请更换或升级浏览器!

Node.js 定时任务调度实战与踩坑记录

tenfei
tenfei
发布于2026-08-13 11:15 阅读15次
Node.js 定时任务调度实战与踩坑记录
分享 Node.js 生产环境定时任务调度的实战经验,对比 setInterval、node-cron、cron 等方案的优劣,深入剖析时区错乱、任务重叠、进程重启丢任务等常见问题,附完整代码和解决方案。
# Node.js 定时任务调度实战与踩坑记录 ## 前言 从简单的数据备份到复杂的报表推送,定时任务几乎每个后端项目都会用到。Node.js 生态里做定时任务的方式很多,但生产环境真正稳定跑起来,有不少坑要填。本文整理一套经过实战验证的方案。 ## 方案对比 | 方案 | 优点 | 缺点 | |------|------|------| | setInterval | 零依赖 | 无法指定具体时间、无容错 | | node-cron | 表达式灵活、轻量 | 单进程、重启丢任务 | | cron (系统) | 稳定可靠、独立进程 | 与代码分离、部署麻烦 | | BullMQ | 队列、重试、持久化 | 依赖 Redis、偏重 | | Agenda | 持久化、延迟任务 | 依赖 MongoDB | ### 什么时候用哪个 - 简单的一次性延迟任务:setTimeout - 单机轻量定时任务:node-cron - 需要可靠性保证的关键任务:系统 cron + 独立脚本 - 海量异步任务队列:BullMQ ## 用 node-cron 实现基础调度 ```javascript const cron = require("node-cron"); // 每天凌晨3点执行数据备份 cron.schedule("0 3 * * *", async () => { try { await backupDatabase(); console.log("备份完成", new Date()); } catch (err) { console.error("备份失败", err); } }); // 每小时的第15分钟执行 cron.schedule("15 * * * *", () => { cleanTempFiles(); }); ``` cron 表达式格式:分 时 日 月 星期(0或7=周日)。 ## 踩坑1:时区错乱 node-cron 默认用服务器的本地时区。如果服务器是 UTC 而你想按北京时间执行,时间会差8小时。 ```javascript const cron = require("node-cron"); // 显式指定时区,避免歧义 cron.schedule("0 3 * * *", task, { timezone: "Asia/Shanghai" }); ``` 经验:任何定时任务都显式声明 timezone,不要依赖服务器默认配置。 ## 踩坑2:任务重叠执行 如果上一个任务还没跑完,下一个周期又到了,任务会并发执行,轻则数据错乱,重则打爆资源。 ```javascript let running = false; cron.schedule("*/5 * * * *", async () => { if (running) { console.log("上次任务未完成,跳过本次"); return; } running = true; try { await heavyTask(); } finally { running = false; } }); ``` 关键:用互斥锁(布尔标志或 Redis 分布式锁)防止重叠。 ## 踩坑3:进程重启丢任务 node-cron 是内存调度,进程重启后任务状态丢失。如果需要"错过的任务补跑",就要引入持久化。 方案:把"该跑没跑的任务"记录到数据库,启动时检查补跑: ```javascript async function recoverMissedTasks() { // 查询上次执行时间 const lastRun = await getLastRunTime(); const now = new Date(); // 如果距离上次超过一个周期,说明有漏跑 if (now - lastRun > PERIOD_MS) { await runTask(); } } recoverMissedTasks(); ``` ## 踩坑4:未捕获的 Promise 异常 异步任务里抛出的异常如果不捕获,会导致 Promise 未处理,甚至在某些 Node 版本下直接崩溃进程。 ```javascript // 统一包装,所有异常都被捕获 cron.schedule("0 3 * * *", async () => { try { await task(); } catch (err) { // 记录日志 + 告警 logger.error("任务执行失败", err); notifyAdmin(err); } }); // 兜底:全局未捕获异常处理 process.on("unhandledRejection", (reason) => { logger.error("未处理的Promise异常", reason); }); ``` ## 生产环境推荐架构 对于关键任务,建议采用"系统 cron 触发 Node 脚本"的架构: ``` # crontab -e # 每天凌晨3点执行备份脚本 0 3 * * * cd /app && node scripts/backup.js >> /var/log/backup.log 2>&1 ``` ```javascript // scripts/backup.js —— 独立脚本,只做一件事 async function main() { await backupDatabase(); console.log("备份完成"); } main() .then(() => process.exit(0)) .catch((err) => { console.error(err); process.exit(1); }); ``` 优势: - 与主应用解耦,互不影响 - 系统 cron 可靠性高,不易丢任务 - 日志独立,便于排查 - 退出码明确,方便监控告警 ## 小结 定时任务虽小,但生产环境要做好四点:显式时区、防重叠、持久化补跑、异常兜底。轻量场景用 node-cron 够了,关键任务建议上系统 cron + 独立脚本。

2

0

文章点评
Copyright © from 2021 by namoer.com
458815@qq.com QQ:458815
蜀ICP备2022020274号-2