You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js如何实现每日触发日期校验 向生日用户自动发送祝贺邮件

现有代码问题分析

  1. setInterval绝对不能放在路由回调里:每次有请求访问/send-email接口,都会新建一个独立的定时器,请求次数多了会出现大量重复定时器,不仅会重复发送邮件,还会造成内存泄漏,拖慢服务性能。
  2. HTTP请求有默认超时时间,你在定时器里返回res.status().jsonp(),第一次定时器触发返回响应后,当前请求的生命周期就结束了,后续定时器再触发时调用res相关方法会直接抛出错误,服务有崩溃风险。

关于setInterval的性能问题

单个每小时触发一次的setInterval几乎不会产生性能开销,在Windows Server 2012上运行完全没有问题,只要避免重复创建多个定时器即可。


正确实现方案

方案1:代码内维护定时任务(适合轻量场景)

定时器全局初始化一次即可,路由只保留手动触发功能,不要嵌入定时逻辑:

const express = require("express");
const app = express();
const excel = require('./public/scripts/readExcel.js');
const email = require('./services/email/sendEmail.js');

// 服务启动时全局初始化唯一的定时器,每小时执行一次发送逻辑
const birthdayEmailTimer = setInterval(() => {
  email.sendEmail()
    .then((result) => {
      console.log('生日邮件发送成功:', result)
    }).catch((err) => {
      console.error('生日邮件发送失败:', err);
    });
}, 3600000);

// 仅作为手动触发接口,不包含定时逻辑
app.post('/send-email-manual', async (req, res)=>{
  try {
    const result = await email.sendEmail();
    res.status(200).json({ success: true, data: result });
  } catch (err) {
    res.status(500).json({ success: false, msg: err.message });
    console.error('手动触发发送失败', err);
  }
});

app.listen(4000, ()=>{
  console.log("服务运行在 -> http://localhost:4000");
})

方案2:系统层面定时任务(更稳定,推荐)

代码内只保留手动触发的接口,不用写定时器,直接用Windows Server 2012自带的任务计划程序新建定时任务:每天固定时间(比如早上9点)执行curl命令调用你写的手动触发接口即可。
这种方案不需要依赖代码内的定时器计时,就算服务重启也不会出现计时偏移、漏执行的问题,稳定性更高。


额外优化建议

  • 加去重逻辑:每次发送邮件后,记录当天已经发送过的用户标识,避免当天多次触发任务时重复给用户发邮件。
  • 加错误告警:如果发送失败,可以增加管理员告警通知,方便及时排查问题。
  • 如果后续用户数据量增长,可以把Excel数据同步到轻量数据库中,查询效率会比直接读Excel高很多。

内容的提问来源于stack exchange,提问作者Jorge Rojas C.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 07:57:02