使用 Swiftmailer 群发邮件

Bulk Email with Swiftmailer

我正在使用 SwiftMailer 发送批量电子邮件。目前,我使用代码

$transport = Swift_SmtpTransport::newInstance('*****', 25);
$transport->setUsername('***');
$transport->setPassword('***');

$mailer = Swift_Mailer::newInstance($transport);
$message = Swift_Message::newInstance();
$message->setSubject($derBetreff);

$bbc= array('1@web.de','2@web.de','3@web.de',...,'1000@web.de');

$message->setFrom(array('my@email.de' => 'My Name'));           
$message->setTo('my@email.de');
$message->setBcc($bcc);
$message->setBody('Hi this is my email');           
$message->attach(Swift_Attachment::fromPath('myFile.pdf'));     

// Send the message
$result = $mailer->send($message);
echo $result;

我只给自己发了一封电子邮件,并在密件抄送中添加了约 1000 人。

执行代码并发送一封电子邮件大约需要 9 分钟,它 returns 一条 'success' 消息。但是,我的 max_execution_time 在我的 php.ini 文件中只设置为 30 秒。

我的第一个问题是:为什么 max_execution_time 没有停止我的 SwiftMailer 脚本?

其次,我发现 AntiFlood Plugin for Swiftmailer 可以帮助发送大量电子邮件。下面的脚本通过先发送 100 封电子邮件然后暂停 30 秒并继续发送接下来的 100 封电子邮件等方式向每个成员发送一封电子邮件。我读到这是避免被标记为垃圾邮件的良好做法。

我的第二个问题是:AntiFlood 插件是否需要非常长的执行时间才能工作? 例如,如果我使用下面给出的脚本发送 1000 封电子邮件并且仅考虑到暂停,那么脚本至少运行了 4.5 分钟,对吧?

// Create the Mailer using any Transport
$mailer = Swift_Mailer::newInstance(
  Swift_SmtpTransport::newInstance('smtp.example.org', 25)
);

// Use AntiFlood to re-connect after 100 emails
$mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100));

// And specify a time in seconds to pause for (30 secs)
$mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30));

// Continue sending as normal
for ($lotsOfRecipients as $recipient) {
  ...

  $mailer->send( ... );
}

要发送批量电子邮件,您应该使用像 RabbitMQ 这样的 Broker 服务。您必须创建 2 个队列,一个用于合并电子邮件,另一个用于使用 Swiftmailer 发送电子邮件。每封电子邮件都是一条 rabbitMq 消息。

首先,脚本本身不会发送电子邮件。这就是为什么如果您的服务器在 Linux 上运行,max_execution_time 不会受到影响,请参阅 docs:

The set_time_limit() function and the configuration directive max_execution_time only affect the execution time of the script itself. Any time spent on activity that happens outside the execution of the script such as system calls using system(), stream operations, database queries, etc. is not included when determining the maximum time that the script has been running. This is not true on Windows where the measured time is real.

因此,当您一次发送多封邮件时,使用 Anti-Flood 插件是个好主意。

不过,我认为更好的做法是将电子邮件存储在数据库中,让 cronjob 每 Y 分钟从数据库发送 X 封邮件。这样,您就没有需要花费几分钟的加载脚本(如果页面重新加载,则有重新发送的危险)。如果您使用像 Laravel 这样的框架,那么您可以使用内置的 mail-query 功能,它正是这样做的。