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

Nodemailer发送邮件频繁ETIMEDOUT:偶发成功但超时问题求助

Nodemailer邮件发送偶发成功、频繁超时(ETIMEDOUT)问题排查求助

问题概述

邮件仅能偶尔成功发送,多数情况下会卡在“Resolved”状态后触发超时(已设置超时时间),且频繁出现ETIMEDOUT错误。触发方式包括ReactJS站点按钮点击,以及用Thunder Client直接调用API,结果一致。控制台可见邮件最终发送成功但随后超时。

相关代码

创建Transporter的函数

const createTransporter = (serviceSettings) => {
// Define which service to use
// gmail    = Use gmail's service (must have GmailSMTPUser and GmailSMTPAppPass setup)
// ll       = Use LumiereLabs's service
const emailService = serviceSettings.use
// Configure the transporter

switch (emailService) {
    case "gmail":
        return nodemailer.createTransport({
            service: 'gmail',
            auth: {
                user: serviceSettings.services.gmail.user,
                pass: serviceSettings.services.gmail.pass
            },
            logger: true,
            debug: true,
            maxConnections: 5, // Maximum number of connections to keep in the pool
            maxMessages: 100, // Maximum number of messages to send per connection
            rateLimit: 10, // Maximum number of messages to send per second
            pool: true, // disable connection pooling
            connectionTimeout: 10000,  // 10 seconds
            socketTimeout: 10000,      // 10 seconds
            greetingTimeout: 5000      // 5 seconds    
        })
    break

    case "ll":
        return nodemailer.createTransport({                    
            host: 'smtp.lumierelabs.org',
            port: 587,
            secure: false,       // true for 465, false for others
            requireTLS: true,
            auth: {
                user: serviceSettings.services.ll.user,
                pass: serviceSettings.services.ll.pass
            },
            tls: {
                rejectUnauthorized: false // if using self-signed certificates
            },
            logger: true,
            debug: true,
            maxConnections: 5, // Maximum number of connections to keep in the pool
            maxMessages: 100, // Maximum number of messages to send per connection
            rateLimit: 10, // Maximum number of messages to send per second
            pool: true, // disable connection pooling
            connectionTimeout: 10000,  // 10 seconds
            socketTimeout: 10000,      // 10 seconds
            greetingTimeout: 5000      // 5 seconds                
        })
    break
}

邮件发送处理函数

const sendEmail = async (mailOptions, successMessage, res, serviceSettings) => {
let toasts = []

const transporter = createTransporter(serviceSettings)
            
try {
    let info = await transporter.sendMail(mailOptions)

    // Send to notification
    toasts.push({ message: successMessage, object: { sentEmail: true }, type: 'success' })

    // Send to console
    console.log('Email sent: ' + info.response)
} catch (error) {
    // Send to notification
    toasts.push({ message: `Error sending mail: ${error}`, object: { sentEmail: false },  type: 'error'})

    // Send to console
    console.error('Error sending email: ', error)

    // Rethrow error to handle it upstream
    throw error
} finally {
    transporter.close() // close the connection pool

    // return something
    if(toasts.length > 0) {
        // Toasts found!
        // Check for type
        if (toasts[0].type === "error") {                                               
            // Error                                                                        
            if (!res.headersSent) {
                return res.status(400).json(toasts)
            } else {
                console.warn('Attempted to send a response after headers were already sent.');
            }                      
        } if (toasts[0].type === "unauthorized") {
            // Unauthorized
            if (!res.headersSent) {
                return res.status(401).json(toasts)
            } else {
                console.warn('Attempted to send a response after headers were already sent.');
            }                      
        } else {
            if (!res.headersSent) {
                return res.status(200).json(toasts)
            } else {
                console.warn('Attempted to send a response after headers were already sent.');
            }                      
        }
    }
}

该函数在ExpressJS后端的userController.js中通过await sendEmail(mailOptions, successMessage, res, serviceSettings)调用。

环境信息

  • Node版本:22.2.0
  • NPM版本:10.7.0
  • 开发系统:Windows 11
  • SMTP服务器系统:Ubuntu 22.04(搭载Postfix和Dovecot)

已尝试的排查措施

  • 从自建SMTP服务器(smtp.lumierelabs.org)切换至Gmail服务,问题依旧,排除SMTP服务器问题
  • 搭建独立测试应用仅使用Nodemailer,问题复现
  • 重启电脑,无改善
  • 通过命令行telnet测试smtp.lumierelabs.org,服务器正常
  • 重新安装Nodemailer
  • 搜索相关问题未找到类似案例
  • 关闭Windows Defender防火墙,无变化

恳请各位提供解决方案或排查思路。

内容的提问来源于stack exchange,提问作者FinuxFirit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 08:37:04