我有一个系统,试图发送批量电子邮件写在NodeJS。
它使用两个模块: fs (构建到节点v0.8.18)和nodemailer (来自NPM,v0.3.42)。
现在,在使用node newsletter.js执行时,有时它会完成并完成,但有时它会在随机点随机失败,出现以下错误:
stream.js:81
throw er; // Unhandled stream error in pipe.
^
Error: write EPIPE
at errnoException (net.js:770:11)
at Object.afterWrite (net.js:594:19)据我所知,发生EPIPE错误是因为连接的另一端被丢弃,然后我们试图写入该连接。有关此EPIPE错误的所有现有报告都在打开http连接或类似的上下文中。
在下面这两件事情中,可能导致错误的是fs.readFileSync In NewsletterEmail或mailer.send in NewsleterMailer。更有可能的是,错误在mailer.send中,nodemailer正在某个地方打开一个连接。但是,没有错误是通过抛出或回调错误参数被传递回的,因此似乎无法看到是什么导致了错误。
大多数人建议定义错误处理程序。但是,nodemailer或fs模块中没有允许我定义错误处理程序的任何内容。来自mailer.send调用的nodemailer中的回调确实在回调中传递了一个错误参数,但是这个特定的错误并没有通过它。
我尝试了以下几点:
fs.readFileSync和nodemailer传输sendEmail调用时添加try/catch。触发此错误的代码如下:
var nodemailer = require('nodemailer');
var fs = require('fs');
/**
* Provides a way to build newsletters when given a folder containing
* the relevant template and images in a standard format. This folder
* must contain a newsletter.html file, a newsletter.txt file and a images
* directory containing any images.
*
* @param {Object} settings The full folder path to the
*/
function NewsletterEmail(newsletterGroup, newsletterName)
{
var folder = '/var/newsletters/' + newsletterGroup + '/' + newsletterName;
this._html = fs.readFileSync(folder + '/newsletter.html', 'utf-8');
this._text = fs.readFileSync(folder + '/newsletter.txt', 'utf-8');
}
NewsletterEmail.prototype.getSubject = function()
{
return 'Testing';
}
/**
* Generates the HTML version of a newsletter.
*
* @return {String}
*/
NewsletterEmail.prototype.buildHTML = function(email)
{
var htmlPart = this._html;
return htmlPart;
}
/**
* Generates the text counterpart of a newsletter.
*
* @return {String}
*/
NewsletterEmail.prototype.buildText = function(email)
{
var textPart = this._text;
return textPart;
}
/**
* Creates a NewsletterEmail from the given folder.
*
* @param {String} folder The folder containing the html template, text template and images for a newsletter.
* @return {NewsletterEmail}
*/
NewsletterEmail.create = function(newsleterGroup, newsletterName) {
return new NewsletterEmail(newsleterGroup, newsletterName);
}
function NewsletterMailer(fromEmail)
{
this._from = fromEmail;
this._transport = nodemailer.createTransport('sendmail');
}
NewsletterMailer.prototype = {
send: function(email, newsletterEmail, callback) {
var mailOptions = {
to: email,
from: this._from,
subject: newsletterEmail.getSubject(),
html: newsletterEmail.buildHTML(email),
text: newsletterEmail.buildText(email)
};
this._transport.sendMail(mailOptions, callback);
},
close: function() {
this._transport.close();
}
}
function Newsletter()
{
this._id = 1;
this.countSent = 0;
this.emailsToSend = ['email1@example.com', 'email2@example.com', 'email3@example.com', 'email4@example.com', 'email5@example.com', 'email6@example.com'];
}
Newsletter.prototype.send = function() {
var newsletter = this;
var newsletterEmail = NewsletterEmail.create('company1', '2013-01-24-mynewsleter');
var mailer = new NewsletterMailer('company@example.com');
function sendEmail() {
var email = newsletter.emailsToSend.pop();
mailer.send(email, newsletterEmail, function(mailerErr) {
if (mailerErr) {
console.log('Mailer error: ', mailerErr);
}
newsletter.countSent++;
console.log('progress ' + newsletter.countSent);
if (newsletter.emailsToSend.length > 0) {
sendEmail();
}
else {
mailer.close();
console.log('complete');
}
});
}
sendEmail();
}
var nl = new Newsletter();
nl.send();还有其他人遇到过类似的错误吗?您是否有任何调试技巧或可能的解决方案。
现在用堆栈跟踪给im一个死胡同。下面是strace的输出。似乎总是先死在邮件的边界上:
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
write(8, "------Nodemailer-0.3.42-?=_1-136"..., 131) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=13813, si_uid=0} ---
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=13818, si_status=0, si_utime=0, si_stime=0} ---
write(4, "\1\0\0\0\0\0\0\0", 8) = 8
rt_sigreturn() = -1 EPIPE (Broken pipe)
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
write(8, "<!DOCTYPE HTML PUBLIC =22-//W3C/"..., 18098) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=13813, si_uid=0} ---
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
close(8) = 0
epoll_wait(3, {{EPOLLIN|EPOLLHUP, {u32=9, u64=4294967305}}, {EPOLLIN|EPOLLHUP, {u32=11, u64=4294967307}}, {EPOLLIN, {u32=4, u64=4294967300}}}, 64, 0) = 3
epoll_ctl(3, EPOLL_CTL_MOD, 9, {EPOLLIN, {u32=9, u64=4294967305}}) = 0
epoll_ctl(3, EPOLL_CTL_MOD, 11, {EPOLLIN, {u32=11, u64=4294967307}}) = 0
read(4, "\1\0\0\0\0\0\0\0", 8) = 8
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED|WCONTINUED, NULL) = 13818
ioctl(1, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff2d6f4340) = -1 EINVAL (Invalid argument)
fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
write(1, "progress 1\n", 11progress 1
) = 11
socketpair(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC, 0, [7, 8]) = 0
socketpair(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC, 0, [10, 12]) = 0
socketpair(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC, 0, [13, 14]) = 0
pipe2([15, 16], O_NONBLOCK|O_CLOEXEC) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f03a4b35a10) = 13820
close(16) = 0
poll([{fd=15, events=POLLIN|POLLHUP}], 1, -1) = 1 ([{fd=15, revents=POLLHUP}])
close(15) = 0
close(7) = 0
ioctl(8, FIONBIO, [1]) = 0
close(12) = 0
ioctl(10, FIONBIO, [1]) = 0
close(14) = 0
ioctl(13, FIONBIO, [1]) = 0
wait4(-1, 0x7fff2d6f529c, WNOHANG|WSTOPPED|WCONTINUED, NULL) = 0
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
brk(0x932000) = 0x932000
read(11, "", 65536) = 0
close(11) = 0
read(9, "", 65536) = 0
futex(0x7f039c0008c8, FUTEX_WAKE_PRIVATE, 1) = 1
close(9) = 0
write(2, "\n", 1
) = 1
write(2, "events.js:71\n", 13events.js:71发布于 2013-02-28 14:46:00
这是由75个字符的nodemailer模块包装行造成的。一行的长度正好是76个字符,结尾有一个点。这个点被包裹在自己的线上。
对于SMTP服务器,行上的点本身表示消息的结束,连接被关闭。这导致了连接的过早关闭,随后的写入由于EPIPE错误而失败。
在nodemailer维护人员的帮助下,通过在sendmail调用中添加hte -i标志,现在已经修复了问题0.3.43,它告诉sendmail允许行带有一个点。
在这里可以获得更多的详细信息:https://github.com/andris9/Nodemailer/issues/141。
https://stackoverflow.com/questions/15118222
复制相似问题