NodeJS 延迟发送电子邮件

NodeJS sending e-mails with a delay

我正在使用 Nodemailer 在我的 NodeJS/Express 服务器中发送邮件。我不想直接发送邮件,而是想等 20 分钟再发送邮件。我认为这比直接发送邮件感觉更个性化。

但我不知道如何实现。我想我不需要像这样的 NodeJS cronjob 这样的东西 NodeCron 包,或者我需要吗?

router.post('/', (req, res) => {
    const transporter = nodemailer.createTransport(smtpTransport({
      host: 'smtp.gmail.com',
      port: 465,
      auth: {
          user: 'noreply@domain.nl',
          pass: 'pass123'
      }
    }));

    const mailOptions = {
      from: `"${req.body.name}" <${req.body.email}>`,
      to: 'info@domain.nl',
      subject: 'Form send',
      html: `Content`
    };

    transporter.sendMail(mailOptions, (error, info) => {
      if (error) res.status(500).json({ responseText: error });
      res.status(200).json({ responseText: 'Message send!' });
    });
  }
}); 

我的路由器如上图所示。因此,如果调用 post,我希望此请求等待 20 分钟。我不想使用 cronjob 只执行一次 post,但有一点延迟。有关如何执行此操作的任何建议?

有些人可能会来这里告诉您使用外部队列系统和 bla bla...但您可以简单地使用普通的旧 Javascript 将发送 20*60*1000 毫秒安排到未来开始做事。 :)

但是您的代码存在问题:您在等待邮件程序成功,然后再向用户发送 200 - 'Message sent' 响应。叫我疯子,但我很确定用户不会盯着浏览器 window 20 分钟,所以你可能必须尽快回答,然后安排邮件。修改您的代码:

router.post('/', (req, res) => {
    const DELAY = 20*60*1000 // min * secs * milliseconds
    const transporter = nodemailer.createTransport(smtpTransport({
      host: 'smtp.gmail.com',
      port: 465,
      auth: {
          user: 'noreply@domain.nl',
          pass: 'pass123'
      }
    }));

    const mailOptions = {
      from: `"${req.body.name}" <${req.body.email}>`,
      to: 'info@domain.nl',
      subject: 'Form send',
      html: `Content`
    };

    res.status(200).json({ responseText: 'Message queued for delivery' });

    setTimeout(function(){
      transporter.sendMail(mailOptions, (error, info) => {
        if (error) 
          console.log('Mail failed!! :(')
        else
          console.log('Mail sent to ' + mailOptions.to)
      }),
      DELAY
    );
  }
}); 

然而,此解决方案可能存在许多缺陷。如果您预计该端点上的流量很大,您最终可能会收到许多会吃掉堆栈的预定回调。此外,如果出现故障,用户当然不会知道。

如果这是一个大型/严肃的项目,请考虑使用那个 cronjob 包或使用外部存储机制,您可以在其中对 "pending" 消息进行排队(Redis 可以做到,而且非常简单),并且有一个不同的从那里处理读取任务并执行电子邮件发送。

编辑:在您的代码中看到了更多内容。

1) 您可能不需要在 POST 处理程序中创建新的 transport,在外部创建它并重新使用它。

2) 除了上述问题,如果您的服务器崩溃,将永远不会发送电子邮件。

3) 如果您仍想在单个 Node.js 应用程序中执行此操作,而不是为每个对此端点的请求安排一封电子邮件,您最好存储电子邮件数据(从、到、主题,正文)某处 并每 20 分钟安排一个函数,该函数将获取所有待处理的电子邮件,一个接一个地发送,然后在 20 分钟后重新安排到 re-run。这将使您的内存使用率保持在较低水平。服务器崩溃仍然会丢失所有电子邮件,但如果您将 REDIS 添加到组合中,那么您可以在应用程序启动时从 REDIS 中简单地获取所有待处理的电子邮件。

答案可能太多了,如果不需要,抱歉! :)

我认为 CharlieBrown 的回答是正确的,因为我在阅读问题时脑子里有两个答案,我感谢他简化了我的答案以替代他的答案。

setTimeout其实是个好主意,但它有一个缺点:在有任何理由停止服务器代码(服务器重启、模块安装、文件管理等)的情况下你的回调计划在 setTimeout 的时间参数结束时将不会执行,部分用户将不会收到电子邮件。

如果上述问题是 serious-enough,那么您可能希望将计划发送的电子邮件存储在数据库或 Redis 中,并使用 cron 作业定期检查电子邮件集并发送电子邮件(如果有)有一些。

我认为这个答案或 CharlieBrown 的答案应该足以满足您的需求,具体取决于您的喜好和需要。