【问题标题】:Nodemailer doesn't seem to be freeing up TCP Ports after a mail has been sent发送邮件后,Nodemailer 似乎没有释放 TCP 端口
【发布时间】:2023-01-04 19:07:19
【问题描述】:

如问题所述,我的群发邮件服务器出现问题。 因此,对于一些背景知识,我们的这个电子邮件应用程序在 CRON 作业上运行,每小时发送一次邮件。 每 12 小时,我们发送最大的邮件,大约有 8 个不同的组,每个组可以有 2000 到 4000 封邮件需要发送。

这个应用程序是用 Nodejs 构建的,并使用 Nodemailer 来处理邮件的发送。

我一段时间以来注意到的是,服务器每隔一段时间就会基本上“冻结”,并且应用程序的内存使用量会缓慢攀升。

Windows 事件日志在本应运行最后一批电子邮件后显示此错误,“由于所有此类端口都在使用中,从全局 TCP 端口空间分配临时端口号的请求失败。”

我浏览了 Nodemailer 网站上的 documentation 并应用了以下更改

  1. 使用池化 SMTP - 这目前仍在应用程序中
  2. 将 maxMessages 设置为无穷大 - 我已经删除了它,因为它似乎没有帮助
  3. 将 maxConnections 设置为 20 - 也被删除,因为它没有任何区别
  4. 使用专用队列管理器——这是我最后的尝试,我使用了 RabbitMQ 并应用了他们的发布/订阅模型。

    RabbitMQ 已经提高了很多性能,但它仍然没有解决问题。

    发布功能

    const PublishMails = (mailObj) => {
        return new Promise((resolve, reject) => {
            var publishMailResult = {};
    
            if (mailObj.length > 0) {
                var connection = global.RabbitMQConnection;
                connection.createChannel((err, channel) => {
                    if (err) {
                        publishMailResult.Result = false;
                        publishMailResult.Message = err.stack;
                        resolve(publishMailResult);
                        //return process.exit(1);
                    }
    
                    channel.assertQueue(config.RabbitMQ.Queue_EmailQueue, {
                        durable: true
                    }, err => {
                        if (err) {
                            publishMailResult.Result = false;
                            publishMailResult.Message = err.stack;
                            resolve(publishMailResult);
                            //return process.exit(1);
                        }
    
                        var mailData = {}
    
                        for (var x = 0; x < mailObj.length; x++) {
                            mailData.from = 'XXX@XXX.com';
                            mailData.to = mailObj[x].Email;
                            mailData.firstName = mailObj[x].FirstName;
                            mailData.login = mailObj[x].Login;
                            mailData.email = mailObj[x].Email;
                            mailData.mailID = mailObj[x].MailID;
                            mailData.sendID = mailObj[x].SendID;
                            mailData.subject = "Email Message";
                            mailData.template = 'EmailTempLate';
    
                            channel.sendToQueue(config.RabbitMQ.Queue_EmailQueue, 
                           Buffer.from(JSON.stringify(mailData)), {
                                persistent: true,
                                contentType: 'application/json'
                            });
    
                            if (x === mailObj.length - 1) {
                                channel.close();
                                publishMailResult.Result = true;
                                publishMailResult.Message = "All mails successfully published.";
                                resolve(publishMailResult);
                            }
                        }
                    });
                })
            } else {
                publishMailResult.Result = false;
                publishMailResult.Message = "No mails were received - Mails Not Published.";
                resolve(publishMailResult);
            }
    
        });
    }
    

    订阅功能

    const SubscribeMails = (mailObj) => {
        return new Promise((resolve, reject) => {
            if (mailObj.PublishMailResult.Result == true) {
                var options = {
                    viewEngine: {
                        extname: '.html',
                        layoutsDir: 'views/email/',
                        defaultLayout: 'Email_Template'
                    },
                    viewPath: 'views/email',
                    extName: '.html'
                };
    
                var transporter = nodemailer.createTransport(smtpTransport({
                    host: 'XXX.XXX.XXX.XX',
                    port: 25,
                    pool: true
                }));
    
                transporter.use('stream', require('nodemailer-dkim').signer({
                    domainName: 'XXX.com',
                    keySelector: 'main',
                    privateKey: 'XXXX'
                }));
    
                transporter.use('compile', hbs(options));
    
                var connection = global.RabbitMQConnection;
                connection.createChannel((err, channel) => {
                    if (err) {
                        console.error(err.stack);
                        return process.exit(1);
                    }
    
                    channel.assertQueue(config.RabbitMQ.Queue_EmailQueue, {
                        durable: true
                    }, err => {
                        if (err) {
                            console.error(err.stack);
                            return process.exit(1);
                        }
    
                        channel.prefetch(1);
    
                        channel.consume(config.RabbitMQ.Queue_EmailQueue, data => {
    
                            if (data === null) {
                                return;
                            }
    
                            let mail = JSON.parse(data.content.toString());
                            transporter.sendMail({
                                from: mail.from,
                                to: mail.to,
                                subject: mail.subject,
                                template: mail.template,
                                context: {
                                    FirstName: mail.firstName,
                                    Email: mail.email,
                                    MailID: mail.mailID,
                                    SendID: mail.sendID,
                                }
                            }, (err, info) => {
                                if (err) {
                                    console.error(err.stack);
                                    return channel.nack(data);
                                }
    
                                channel.ack(data);
    
                                channel.checkQueue(config.RabbitMQ.Queue_EmailQueue, function (checkErr, queueData) {
                                    if (queueData != null) {
                                        if (queueData.messageCount == 0) {
                                            channel.close();
    transporter.close(); // Added in to test if this free's up TCP ports - Didn't help
                                        }
                                    }
                                });
                            });
                        });
    
                        resolve(true);
                    });
                });
            }
        });
    }
    

    真的感觉我应该以某种方式手动关闭这些 TCP 连接,但我没有在文档中看到任何关于此的内容,也没有在我看到的任何示例代码中看到它。

    我正在添加启动此过程的 Cron 作业,以帮助调试此问题。

    var cronPCSec = '0';
    var cronPCMin = '58';
    var cronPCHour = '*';
    var cronPCDay = '*';
    var cronPCMonth = '*';
    var cronPCDayOfWeek = '0-6';
    var cronPCfulltimes = "" + cronPCSec + " " + cronPCMin + " " + cronPCHour + " " + cronPCDay + " " + cronPCMonth + " " + cronPCDayOfWeek + "";
    
    
    var MailerCronJob = new CronJob({
        cronTime: cronPCfulltimes,
        onTick: function () {
            let objCronJob = {};
    
            modelPC.GetMails().then(function (mail) {
                return mail;
            }).then(PublishMails).then(function (PublishResult) {
                objCronJob.PublishMailResult = PublishResult;
                return objCronJob;
            }).then(SubscribeMails).then(function (result) {
                console.log("Completed Successfully");
            }).catch(err => {
                console.log("Failed");
                console.log(err)
            });
        },
        start: false
    });
    
    MailerCronJob.start();
    

    谢谢

【问题讨论】:

  • 您确定是来自邮件程序的 tcp 连接保持打开状态吗?根据 NodeMailer 文档,除非您覆盖这些数字,否则每个池最多打开 5 个连接,而且您似乎没有这样做。
  • 嗨@Evert,感谢您的回复。是的,我确定它来自 Mailer。当我登录到我们的邮件服务器时,只有在我重新启动 nodemailer 服务器后,我才能看到一切都“冻结”了,系统资源被释放,一切都开始正常运行。

标签: javascript node.js cron rabbitmq nodemailer


【解决方案1】:

你能告诉我mailObj中有什么数据吗?

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2010-10-19
  • 1970-01-01
  • 2021-06-10
相关资源
最近更新 更多