【问题标题】:Avoid PHP timeout when sending newsletter发送时事通讯时避免 PHP 超时
【发布时间】:2009-08-17 17:33:35
【问题描述】:

我最近实现了Swiftmailer 库,但是我第一次使用它时,我得到了一个 php 超时错误,即使它只需要向大约 300 人发送邮件..(我应该提到使用 @由于服务器的限制,987654324@ 解决方法不是一个选项)

我的使用方式是这样的:

 $transport = Swift_SmtpTransport::newInstance('smtp.mydomain.com',25);
 $mailer = Swift_Mailer::newInstance($transport);
 $message = Swift_Message::newInstance();
 $message->setSubject($_POST['subj']);
 $message->setFrom(array('email' => 'Mysite'));
 $message->setTo($recipients);
 $message->setBody(strip_tags($messagebody));
 $message->addPart($messagebody, 'text/html');
 $numSent = $mailer->batchSend($message);
 printf("Sent %d messages\n", $numSent);

$recipients 是一个逗号分隔的电子邮件数组(使用来自 mySQL 查询的 explode() 构建)..

我不知道上面代码的哪一部分生成了脚本超时,并且由于我将它用于新闻通讯功能,因此我很难对其进行测试或调试,因此我会非常感谢任何人想出一个(近乎)防弹的解决方案来解决这个问题:)

提前致谢!

拉尔斯

【问题讨论】:

  • 好的,但是你为什么不测量一下哪个语句需要这么长时间呢?例如,您可以在每个语句之后将一些内容写入调试文件。
  • 您确定您的 SMTP 服务器没有限制您吗?当服务器看到大量电子邮件来自您的服务器时,您的邮件可能会受到限制。
  • 我的猜测是它不是 SMTP,但当然,我不能确定..
  • 我们在谈论多少 $recipients?也许你应该看看swiftmailer.org/docs/antiflood-plugin
  • 小于 300 .. :) 我确实看过 swiftmailer.org/docs/antiflood-plugin 但因为我没有将其视为 SMTP 问题(而是作为 PHP 问题)我不认为这将是解决方案.. 毕竟,它可能只会增加原始脚本的执行时间,不是吗?我的意思是,antiflood 插件添加到执行中的暂停只是一个暂停,这会增加整体执行时间..

标签: php timeout swiftmailer


【解决方案1】:

如果您可以访问 cron,请用发送时事通讯所需的内容填充数据库,然后使用运行脚本处理少量邮件的 cron 作业弹出队列。

如果你不这样做,请在脚本末尾写一个 system() 语句,让它调用它自己,将下一个时事通讯作为参数传递,然后死掉。因此,您将根据发送邮件的需要多次执行脚本。

【讨论】:

  • 我确实可以访问 cron(虽然不是免费的),这实际上也可能是个好主意。你认为有多大的“块”(一次有多少邮件)就足够了不创建超时?
  • 我猜不出来,但这里有一个简单的方法可以知道:在没有超时的机器上运行代码并记录它所花费的时间,然后除以邮件数量......如果你没时间,一点一点的尝试,我觉得10封邮件30秒发不完。
【解决方案2】:

限制每封发送邮件的收件人数量。通过 curl 或 cron 使用偏移值调用脚本“n”次,它不会抛出超时。例如设置 $limit = 20。然后调用脚本 n 次:

send.php?offset=0
send.php?offset=20

等等...如果您的根脚本(调用 send.php 脚本)超时,那么您可以继续从上次偏移量发送邮件。

【讨论】:

    【解决方案3】:

    [更新] 下面的数据是我从我的开发框中调用 smtp 服务器的。所以从东欧像样的家庭ADSL 到美国的SMTP 服务器(我的项目Staging 服务器)。当它全部在 Staging 上运行时 - 所以 localhost php+mysql+Postfix 这个宝贝几乎不需要 1 秒就可以向 30 个收件人发送 5 封单独的电子邮件和密件抄送。好风光……

    Swiftmaieler 实际上是同步运行的,并等待 SMTP 服务器发送每封电子邮件,以便它获取包含失败邮件地址的数组。因此,如果您密件抄送 100 个收件人,则总时间为:与服务器通话的时间 + 服务器发送 100 封电子邮件的时间。发送 100 封单独的电子邮件将是 100*(通话时间 + 发送时间)。我的 Postfix 每封电子邮件需要 1 秒,通话时间也是 1 秒,因此发送一封电子邮件需要 2.5 秒。使用标准的 30 秒超时,密件抄送中将有 12 封单独的电子邮件或 20 个收件人。其实都是猜谜游戏:P

    所有这些邮件程序似乎都必须使用 set_time_limit(0) 来设计。对于有 30 秒超时的人来说,最聪明的方法是让“批量发送”页面保持打开状态,并有一个 ifrme+refresh 或 Ajax 机制,每隔 x 调用一个“发送邮件数据包”(一个小的 10 个)方法秒。使用 Ajax,您可以再次调用脚本响应。

    【讨论】:

      【解决方案4】:

      set_time_limit(0); 代码前

      编辑:好的,既然您不能使用set_time_limit() 函数,为什么不将发送电子邮件的代码放在一个函数中并使用register_shutdown_function()?这样,当脚本超时时,它会再次调用该函数。

      【讨论】:

      • 原始海报特别指出这不是一个选项。
      • 我从来没有听说过这个函数 - 将整个东西包装在一个函数中并使用 register_shutdown_function() 实际上可能是一个非常好的主意......我只是认为这个问题可能是相关的更具体地说是 Swiftmailer 配置..
      • 你会如何实现这个?会不会是这样的情况:页面顶部的 register_shutdown_function(sendmail) 然后 function sendmail() { # all the swiftmailer code here }
      • 超时后不会再运行一次函数吗?
      猜你喜欢
      • 2012-01-08
      • 2012-01-07
      • 2012-02-25
      • 2013-11-10
      • 2012-03-08
      • 1970-01-01
      • 2012-06-25
      • 2011-09-11
      • 2015-07-28
      相关资源
      最近更新 更多