Node.js CronJob定时任务无限重复发送短信问题如何解决
问题根因
- Cron表达式配置错误:你写的
* 5 * * * *是6位cron规则,对应秒 分 时 日 月 周,意思是每个小时第5分钟的每一秒都执行任务,相当于每到小时的第5分钟就每秒跑一次,自然会重复触发短信。 - 无发送状态校验:现有逻辑没有记录哪些记录已经发过短信,只要时间差符合条件就触发推送,就算修复了cron表达式,前后两次5分钟间隔的任务也可能命中同一个符合条件的记录。
修复步骤
第一步:修正Cron定时规则
将定时任务的cron表达式改为0 */5 * * * *,实现每5分钟整点执行1次的需求。
第二步:增加发送状态校验
提供两种可选实现方案:
方案A:数据库存储发送状态(推荐,服务重启不丢失数据)
- 在
clients表新增last_sms_sentDATE类型字段,存储最近一次短信发送日期 - 发送短信前增加字段校验,发送成功后更新字段值
修改后的核心代码示例:
// 修正后的cron表达式 var getTexts = new cronJob('0 */5 * * * *', function() { let weekday = ['SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY'][new Date().getDay()] let todayStr = new Date().toISOString().split('T')[0] // 格式化今日日期为YYYY-MM-DD // 查询时同时带出最近发送日期 var viewConformationEmails = `select ${weekday}, last_sms_sent from clients`; ibmdb.open(ibmdbconn, function(err, conn) { if (err) return console.log(err); conn.query(viewConformationEmails, function(err, rows) { if (err) { console.log(err); var today = new Date(); var time = today.getMinutes() + " " + today.getHours() + " * " + "* " + "*" var errorJob = new cronJob(time, function() { client.messages.create({ to: "1234567890", from: '12055578708', body: 'Error with your code: ' + err }, function(err, data) {}); }, null, true); } for (let i = 0; i < rows.length; i++) { let todaysColumn = rows[i][weekday]; // 今日已经发过的直接跳过 if(rows[i].LAST_SMS_SENT && new Date(rows[i].LAST_SMS_SENT).toISOString().split('T')[0] === todayStr) { continue } let userInfoString = todaysColumn.split('|'); const userPhoneNumber = userInfoString[1] const userNameString = userInfoString[2] const trainerNameString = userInfoString[3] let timeSplit = userInfoString[0].split(' ') let minute = timeSplit[0] let hour = timeSplit[1] // 同时判断小时避免跨小时的时间差误判 let today = new Date(); if (today.getHours() === Number(hour) && Math.abs(today.getMinutes() - Number(minute)) <= 5) { client.messages.create({ to: userPhoneNumber, from: '12055578708', body: 'hello ' + userNameString + ', \nYour training session just finished with ' + trainerNameString }, function(err, data) { if (err) { console.log("err: " + err) return } // 发送成功后更新发送日期,将代码中主键相关内容替换为表实际主键字段 conn.query(`update clients set last_sms_sent = '${todayStr}' where 主键字段 = ?`, [当前行主键值], function(updateErr) { if(updateErr) console.log(updateErr) }) console.log(data) }); } else { console.log("not working") } } conn.close(function() {}); }); }); }, null, true)
方案B:内存缓存存储发送状态(无需改表,适合轻量场景)
安装node-cache依赖后,用缓存记录已发送的标识:
const NodeCache = require('node-cache') const smsSentCache = new NodeCache({ stdTTL: 86400 }) // 缓存有效期1天 var getTexts = new cronJob('0 */5 * * * *', function() { let weekday = ['SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY'][new Date().getDay()] var viewConformationEmails = "select " + weekday + " from clients"; ibmdb.open(ibmdbconn, function(err, conn) { if (err) return console.log(err); conn.query(viewConformationEmails, function(err, rows) { if (err) { console.log(err); var today = new Date(); var time = today.getMinutes() + " " + today.getHours() + " * " + "* " + "*" var errorJob = new cronJob(time, function() { client.messages.create({ to: "1234567890", from: '12055578708', body: 'Error with your code: ' + err }, function(err, data) {}); }, null, true); } for (let i = 0; i < rows.length; i++) { let todaysColumn = rows[i][weekday]; let userInfoString = todaysColumn.split('|'); const userTextString = userInfoString[0] const userPhoneNumber = userInfoString[1] const userNameString = userInfoString[2] const trainerNameString = userInfoString[3] // 生成唯一缓存键:手机号+星期+时间规则 const cacheKey = `${userPhoneNumber}_${weekday}_${userTextString}` // 已经发过的直接跳过 if(smsSentCache.has(cacheKey)) continue let timeSplit = userTextString.split(' ') let minute = timeSplit[0] let hour = timeSplit[1] let today = new Date(); if (today.getHours() === Number(hour) && Math.abs(today.getMinutes() - Number(minute)) <= 5) { client.messages.create({ to: userPhoneNumber, from: '12055578708', body: 'hello ' + userNameString + ', \nYour training session just finished with ' + trainerNameString }, function(err, data) { if (err) { console.log("err: " + err) return } // 发送成功后写入缓存 smsSentCache.set(cacheKey, true) console.log(data) }); } else { console.log("not working") } } conn.close(function() {}); }); }); }, null, true)
内容的提问来源于stack exchange,提问作者gianluca
相关产品推荐
相关产品推荐

