![当批量邮件脚本发送数千封电子邮件时,Sendmail 发生异常。Sendmail 对给定进程的电子邮件发送量有限制吗?](https://linux22.com/image/799842/%E5%BD%93%E6%89%B9%E9%87%8F%E9%82%AE%E4%BB%B6%E8%84%9A%E6%9C%AC%E5%8F%91%E9%80%81%E6%95%B0%E5%8D%83%E5%B0%81%E7%94%B5%E5%AD%90%E9%82%AE%E4%BB%B6%E6%97%B6%EF%BC%8CSendmail%20%E5%8F%91%E7%94%9F%E5%BC%82%E5%B8%B8%E3%80%82Sendmail%20%E5%AF%B9%E7%BB%99%E5%AE%9A%E8%BF%9B%E7%A8%8B%E7%9A%84%E7%94%B5%E5%AD%90%E9%82%AE%E4%BB%B6%E5%8F%91%E9%80%81%E9%87%8F%E6%9C%89%E9%99%90%E5%88%B6%E5%90%97%EF%BC%9F.png)
我对我们的网络服务器的问题感到有点绝望。我们的网站是一个拥有大量用户和活动的社区。我们向用户发送他们感兴趣的活动的个性化邮件。我们在处理这些邮件的脚本中遇到了问题。
我们的当需要发送数千封电子邮件时,批量电子邮件脚本会失败。它通常工作正常,但当它必须发送比平时更多的电子邮件(大约 25,000 封电子邮件)时,它会反复抛出异常:
Unable to send mail. mail():
Could not execute mail delivery program '/usr/sbin/sendmail -t -i '
奇怪的是sendmail在其他进程中正常运行比如 Web 服务器,在 PHP (Zend) 中 sendmail 的调用方式也一样。在 PHP 批量邮件脚本中,当大量邮件已经无误地发送时,Sendmail 才会失败。当第一个异常被抛出时,接下来对 sendmail 的调用也会失败。好像已经达到某个队列限制,但仅限于此进程!
PHP脚本代码
PHP 批量邮件脚本主循环执行数千次。每次循环都使用不同的$email
and调用 sendMail $user
:
// Sometimes, hundred thousands iterations
foreach($notifications as $j => $notification){
...
$mail->setNotification($notification);
$this->sendMail($mail, $user);
...
}
$this->sendmail($mail, $user)
调用 Zend 内部方法发送邮件。它调用 PHP 本机方法邮件。
/**
* Send mail using PHP native mail()
*
* @access public
* @return void
* @throws Zend_Mail_Transport_Exception if parameters is set
* but not a string
* @throws Zend_Mail_Transport_Exception on mail() failure
*/
public function _sendMail()
{
...
set_error_handler(array($this, '_handleMailErrors'));
// CALL TO MAIL PHP NATIVE METHOD
$result = mail(
$this->recipients,
$this->_mail->getSubject(),
$this->body,
$this->header,
$this->parameters);
restore_error_handler();
}
if ($this->_errstr !== null || !$result) {
/**
* @see Zend_Mail_Transport_Exception
*/
require_once 'Zend/Mail/Transport/Exception.php';
// HERE THE EXCEPTION IS THROWN
throw new Zend_Mail_Transport_Exception('Unable to send mail. ' . $this->_errstr);
}
}
正在sendmail
运行的进程
查看ps -aux | grep sendmail
批量邮件脚本正常运行时的输出
$ ps -aux | grep sendmail
root 6756 0.0 0.0 62240 2468 ? Ss 18:19 0:08 sendmail: MTA: accepting connections
root 25766 0.0 0.0 62668 3536 ? Ss 22:43 0:00 sendmail: MTA: ./r17Lh1fX025764 eml4.in.gr.: client DATA status
root 30978 0.0 0.0 62460 2876 ? Ss 22:46 0:00 sendmail: MTA: ./r17Lk8li030976 s1.m1r3.onet.pl.: user open
root 31462 0.0 0.0 62672 3536 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkSIg031460 mx2.hotmail.com.: client DATA status
root 31474 0.0 0.0 62672 3540 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkT54031472 mx2.hotmail.com.: client DATA status
root 31494 0.0 0.0 62668 4404 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkUXC031492 gmail-smtp-in.l.google.com.: client RCPT
root 31498 0.0 0.0 62668 3536 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkUn1031496 mx4.hotmail.com.: client DATA status
root 31502 0.0 0.0 62672 3536 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkUti031500 mx3.hotmail.com.: client DATA status
root 31506 0.0 0.0 62672 3500 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkUHw031504 mx4.hotmail.com.: client RCPT
root 31510 0.0 0.0 62672 3496 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkUth031508 mx3.hotmail.com.: client MAIL
root 31514 0.0 0.0 62668 4436 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkVPb031512 gmail-smtp-in.l.google.com.: client DATA status
root 31518 0.0 0.0 62460 2888 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkV9o031516 mx1.hotmail.com.: client EHLO
root 31522 0.0 0.0 62668 4404 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkVD4031520 gmail-smtp-in.l.google.com.: client RCPT
root 31526 0.0 0.0 62460 2852 ? Ss 22:46 0:00 sendmail: MTA: ./r17LkVcF031524 mx3.hotmail.com.: user open
当脚本开始抛出异常时,ps -aux | grep sendmail
输出几乎为空,正如预期的那样
$ ps -aux | grep sendmail
root 6756 0.0 0.0 62240 2468 ? Ss Feb07 0:49 sendmail: MTA: accepting connections
一些问题
我对 sendmail 还很陌生,所以非常感谢任何帮助。如果您需要更多信息,请告诉我。
- 简单来说,sendmail 如何发送邮件?
- sendmail 是否有发送邮件数量的限制(我们的服务器是专用的,托管服务提供商没有设置特定限制)?
- sendmail 是否有任何进程队列来限制给定进程可以发送的邮件数量?
- 百万美元的问题:为什么会抛出异常以及如何解决?:-)
在此先感谢您的帮助!
答案1
当你mail()
在基于 Unix 的系统上调用 PHP 时,它会分叉sendmail
命令并将每个收件人作为命令行上的参数传递。但是命令行中传递的参数长度是有限制的!这是一个非常大的限制,Linux 系统默认为 128KiB,但 25,000 个电子邮件地址远远超出了该限制。
要解决此问题,请分批发送邮件,例如一次发送 1,000 个收件人。您应该会发现将收件人数组分成 1,000 个组并循环遍历它们很简单,但如果不行,请访问我们的姊妹网站堆栈溢出获得编程帮助。
然而,你的错误信息似乎表明你没有通过任何收件人收到该消息。因此,您或许应该查找一下编程错误,在调用时mail()
没有使用recipients
或使用了无效recipients
。
答案2
运行此脚本时,您是否以不同于运行 Web 服务器邮件脚本的用户身份运行它?对于运行脚本的用户来说,这可能是一些简单的事情,例如 ulimit。
答案3
任何邮件系统都会因短时间内发送过多邮件而无法应对。也许您应该在发送一批邮件(例如每 100 条左右)后暂停一会儿。
无论如何,你应该仔细检查你在这里做的事情。我非常怀疑你有成千上万条重要的消息给收件人同时发送。这种行为只会让你陷入人类已知的所有电子邮件黑名单的最深处。