Node.js使用nodemailer发邮件报554 6.6.0错误的原因及解决方法
问题描述
我尝试使用nodemailer模块在Node.js环境下发送邮件,以下是我的代码:
const nodeMailer = require('nodemailer'); const randomSentence = require('random-sentence'); let mailList = [ 'gmans8951@gmail.com', 'hamideh.2020ha@gmail.com', 'mali1370.goli@gmail.com', 'golgolniamilad@gmail.com' ] async function test(){ let transporter = nodeMailer.createTransport({ host: 'smtp.mail.yahoo.com', port: '465', secure: true, auth: { user: 'milad1395@yahoo.com', pass: 'xxxxxxxxxxx' } }); try{ for (let index = 0; index < mailList.length; index++) { let mailBody = randomSentence(); console.log(mailBody); const contact = mailList[index]; await transporter.sendMail({ from: 'Node Mailer Test App <milad1395@yahoo.com>', to: contact, subject: 'test', html: mailBody }) } console.log('All mails sent successfully!'); }catch(err){ console.log(`Error: ${err}`); } } let forCount = 1; for (let index = 0; index < forCount; index++) { test(); }
当forCount变量设为1时程序运行正常,运行输出如下:
$ node debug.js message 0: Ecmiw perpef suchuf runog olu duiduz remis ehere bevu fetuh leh areri gujin kuvug bifa. message 1: Tuemigo lelsojkil we fenob meboceuti rifa ci ewiilu fisif uwois apovev seplep kotpi voug vek. message 2: Suvne goeviru gigiwo dec pitak daiwa edo fifmij ne lad osezo wilomdo ore kebpenu nig zifvi gocpo. message 3: Kibep pevkih cuf jar du lu li loj gicrioke fuwdij fo fo tiho pupaj pogpisu vogo uja. All mails sent successfully!
但我将forCount变量调整为2后,仅能发送部分邮件,随后就会抛出如下报错:
Error: Error: Message failed: 554 6.6.0 Error sending message for delivery.
问题1:该错误出现的原因是什么?
- 核心原因是雅虎个人免费邮箱的SMTP服务有严格的发信频率、并发连接限制。你外层循环调用
test函数时没有加await,当forCount设为2时,两个test函数会并行执行,短时间内发起8封邮件的投递请求,触发了雅虎的反垃圾邮件拦截策略,服务端直接返回554错误拒绝投递。 - 次要原因是代码逻辑不合理:每次执行
test函数都会新建一个SMTP连接实例,并行执行时会同时建立多个连接,远超个人邮箱的并发连接上限,进一步触发服务端的拦截规则。
问题2:如何解决该问题?
按优先级可以选择以下调整方案:
- 调整调用逻辑,避免并行执行发信任务。外层循环调用
test时加await,保证前一轮所有邮件发送完成后再执行下一轮,同时可以在两轮之间加时间间隔避开频率限制,参考修改代码:
(async () => { const forCount = 2; for (let index = 0; index < forCount; index++) { await test(); // 每轮发信结束后等待60秒再执行下一轮,可根据实际情况调整时长 await new Promise(resolve => setTimeout(resolve, 60 * 1000)); } })()
- 复用SMTP连接实例,把transporter的初始化逻辑移到
test函数外部,全局只创建一个连接实例,减少不必要的连接开销,降低被服务端拦截的概率。 - 如果有大量发信需求,不要使用个人免费邮箱,换用专门的商业邮件推送服务,这类服务没有个人邮箱的严格发信限制。
内容的提问来源于stack exchange,提问作者milad
相关产品推荐
相关产品推荐

