从 Zend Framework 应用程序向数百个收件人发送电子邮件的最佳方法是啥?
Posted
技术标签:
【中文标题】从 Zend Framework 应用程序向数百个收件人发送电子邮件的最佳方法是啥?【英文标题】:What's the best approach to sending email to hundreds of recipients from a Zend Framework application?从 Zend Framework 应用程序向数百个收件人发送电子邮件的最佳方法是什么? 【发布时间】:2010-10-21 18:37:27 【问题描述】:我正在尝试为我的应用程序实现一个邮件列表系统。我目前使用Zend_Mail_Transport_Smtp('localhost')
作为我的传输,遍历我的订阅者列表,并向每个订阅者发送一个新的Zend_Mail
。但是,我注意到脚本完成所需的时间会随着订阅者数量的增加而增加。
我确信必须有更专业的方法来执行此操作,包括电子邮件排队。我想理想的方法是让用户填写表格,点击发送,然后立即得到回复说正在发送电子邮件,而不是等待数百封电子邮件完成发送。
我了解Zend_Mail
不会进行任何排序邮件排队。有这方面经验的人可以给我概述一下如何做到这一点吗?我对cron/crontab/cronjobs一无所知,所以如果涉及到,请解释一下过程。
【问题讨论】:
【参考方案1】:为了使用 php 可靠地发送大量电子邮件,您必须使用排队机制。正如其他人所建议的,使用队列的过程如下所示:
遍历您的一组用户,为每个用户创建一封电子邮件,并可能自定义内容 将每个邮件对象传递到队列中,这将延迟发送电子邮件,直到稍后 在某种 cron 脚本中,一次发送几百个队列的内容。 注意:您需要通过查看日志以了解实际发送过程中返回的错误来调整您发送的电子邮件数量。如果您尝试发送太多,我注意到它会达到邮件传输不再接受连接的程度(我正在使用 qmail)您可以使用一些库来执行此操作,PEAR Mail Queue(带有 Mail_Mime)和 SwiftMailer 都允许您创建和排队电子邮件。到目前为止,Zend Mail 只提供电子邮件的创建,而不是排队(稍后会详细介绍)。
我主要有使用PEAR Mail Queue 的经验,并且有一些陷阱。如果您尝试将大量电子邮件排队(例如,循环超过 20,000 个用户并尝试在合理的时间内让他们进入队列),则使用 Mail Mime 的引用打印编码实现非常慢。您可以通过切换到 base64 编码来加快速度。
对于 Zend Mail,您可以编写一个 Zend Mail Transport 对象,将您的 Zend Mail 对象放入 PEAR 邮件队列。我已经成功地做到了这一点,但是需要花点时间才能做到这一点。为此,扩展 Zend Mail Transport Abstract,实现 _sendMail 方法(您将在此将 Zend Mail 对象放入邮件队列)并将传输对象的实例传递给 Zend Mail 对象的 send() 方法或通过 Zend Mail::setDefaultTransport()。
最重要的是,您可以通过多种方式做到这一点,但这需要您进行一些研究和学习。然而,这是一个非常容易解决的问题。
【讨论】:
【参考方案2】:注意:当我第一次阅读您的问题时,我以为它会同时包含数十万封电子邮件。当我仔细检查时,我注意到它实际上说的是数百到数千。我现在懒得更改我的帖子,所以这里有一些警告:根据我的经验,你可能可以在没有商业工具的情况下正常运行到 40K 左右。在大约 10K 时,您将希望遵循“最小”列表,以防止当您开始达到更大的列表大小时出现重大痛苦。不过,我确实建议立即实施。
我之前说过,发送电子邮件有两个方面:
-
技术方面——基本上都是
围绕 smtp 的 RFC
协议、电子邮件格式、DNS
记录等。这是温和的
复杂但可以解决。
神奇的一面——电子邮件传递
管理是巫术。你会得到
沮丧,事情会无缘无故地破裂
明显的原因,你会
考虑离开另一份工作
这不涉及电子邮件。
我建议不要编写自己的批量发件人。我确信 PHP 可以做得很好,但您可能应该把时间花在其他地方。我过去使用并推荐的两个产品是 Strongmail 和 PowerMTA。请注意——它们的价格很高,但我几乎可以保证,从长远来看,你会花更多的钱来构建自己的解决方案。
在 PHP 中编写自己的代码时会遇到的一个问题是节流/焦油坑。在您发送一些消息以减慢您的速度并阻止您发送垃圾邮件后,邮件服务器将开始添加 sleep(30)。
通常,这些商业批量发件人运行 SMTP 协议进行排队。您将继续使用 Zend_Mail,但对其进行硬编码以连接到您的服务器。它会尽可能快地将邮件排队,然后使用它自己的引擎将邮件发送到他们的目的地。
在 100K 列表中,您必须采用电子邮件最佳做法。您至少需要:
SPF 记录,可能还有 DKIM 多个 IP 用于分段流量 - 有 3 个 IP,一个用于您信任的质量地址,一个用于中等风险 IP 地址,一个用于高风险 IP 地址。这种设计有助于最大限度地降低将邮件发送给最佳客户的风险。 正确的反向 DNS 用于发送 IP 地址 使用来自 AOL、hotmail、yahoo 和其他公司的反馈循环来处理垃圾邮件投诉 退订和退回管理 - 确保您正在修剪这些地址 打开/点击跟踪也很重要——如果您是 A 列表中的客户没有打开您的电子邮件,您需要将它们降级为 B 列表等等。这很重要,因为 ISP 会将不活动的帐户变成蜜罐。 Hotmail 以此而闻名。最后,如果你真的很想发送电子邮件,你会想要一些其他工具,比如 Return Path。
【讨论】:
你知道你的东西。不幸的是,你的答案是压倒性的。如何将 Strongmail 或 PowerMTA 与 Zend Mail 结合使用并涵盖您列出的所有要点? 您可以将您的应用程序与 Strongmail 绑定,当我说应用程序时,我指的是数据存储,这意味着您不需要实现任何代码行,您只需与 Strongmail 服务器共享您的用户详细信息并发送数百万封电子邮件:)【参考方案3】:来自 PHP.net 文档。
注意:值得注意的是,mail() 函数不适合循环处理大量电子邮件。此函数为每封电子邮件打开和关闭一个 SMTP 套接字,效率不高。 如需发送大量电子邮件,请参阅 » PEAR::Mail 和 » PEAR::Mail_Queue 包。
Zend Mail 类可能非常好(Zend 的大部分东西都很好)但是如果您想要其他选择。他们来了。
【讨论】:
PEAR::Mail 在我的经验中很慢。 PHPMailer 和 swiftmailer 非常好。【参考方案4】:使用 Zend_Queue 将电子邮件放入队列中以进行异步后台处理。您将需要一个 cron 作业来在后台处理队列。
protected function _enqueueEmail(WikiEmailArticle $email)
static $intialized = false;
if (!$initialized)
$this->_initializeMailQueue("wikiappwork_queue");
$initialized = true;
$this->_mailQueue->send(serialize($email));
protected function _initializeMailQueue()
/* See: 1.) http://framework.zend.com/manual/en/zend.queue.adapters.html and
* 2.) Zend/Queue/Adapter/Db/mysql.sql.
*/
$ini = Zend_Controller_Front::getInstance()->getParam('bootstrap')
->getOptions();
$queueAdapterOptions = array( 'driverOptions' => array(
'host' => $ini['resources']['multidb']['zqueue']['host'],
'username' => $ini['resources']['multidb']['zqueue']['username'],
'password' => $ini['resources']['multidb']['zqueue']['password'],
'dbname' => $ini['resources']['multidb']['zqueue']['dbname'],
'type' => $ini['resources']['multidb']['zqueue']['adapter'] ),
'name' => $ini['resources']['multidb']['zqueue']['queueName'] );
$this->_mailQueue = new Zend_Queue('Db', $queueAdapterOptions);
然后对于 cron 作业,类似的脚本
<?php
use \Wiki\Email\WikiEmailArticle;
// Change this define to correspond to the location of the wikiapp.work/libary
define('APPLICATION_PATH', '/home/kurt/public_html/wikiapp.work/application');
set_include_path(implode(PATH_SEPARATOR, array(
APPLICATION_PATH . '/../library',
get_include_path(),
)));
// autoloader (uses closure) for loading both WikiXXX classes and Zend_ classes.
spl_autoload_register(function ($className)
// Zend classes need underscore converted to PATH_SEPARATOR
if (strpos($className, 'Zend_' ) === 0)
$className = str_replace('_', '/', $className );
$file = str_replace('\\', '/', $className . '.php');
// search include path for the file.
$include_dirs = explode(PATH_SEPARATOR, get_include_path());
foreach($include_dirs as $dir)
$full_file = $dir . '/'. $file;
if (file_exists($full_file))
require_once $full_file;
return true;
return false;
);
// Load and parese ini file, grabing sections we need.
$ini = new Zend_Config_Ini(APPLICATION_PATH .
'/configs/application.ini', 'production');
$queue_config = $ini->resources->multidb->zqueue;
$smtp_config = $ini->email->smtp;
$queueAdapterOptions = array( 'driverOptions' => array(
'host' => $queue_config->host,
'username' => $queue_config->username,
'password' => $queue_config->password,
'dbname' => $queue_config->dbname,
'type' => $queue_config->adapter),
'name' => $queue_config->queuename);
$queue = new Zend_Queue('Db', $queueAdapterOptions);
$smtp = new Zend_Mail_Transport_Smtp($smtp_config->server, array(
'auth' => $smtp_config->auth,
'username' => $smtp_config->username,
'password' => $smtp_config->password,
'port' => $smtp_config->port,
'ssl' => $smtp_config->ssl
));
Zend_Mail::setDefaultTransport($smtp);
$messages = $queue->receive(10);
foreach($messages as $message)
// new WikiEmailArticle.
$email = unserialize($message->body);
try
$email->send();
catch(Zend_Mail_Exception $e)
// Log the error?
$msg = $e->getMessage();
$str = $e->__toString();
$trace = preg_replace('/(\d\d?\.)/', '\1\r', $str);
// end try
$queue->deleteMessage($message);
// end foreach
【讨论】:
【参考方案5】:您应该可以将 PHP 用于数以千计的收件人,但请避免使用其他人指出的 mail()。我已经看到一些为大量邮件(超过 100,000 个收件人)设计的系统开始绕过标准邮件功能并尝试更直接地使用 MTA。即便如此,我也不清楚这是必需的。
使电子邮件专业化更多的是确保格式良好(尽可能使用 HTML 和纯文本),人们可以轻松退订,正确处理退回邮件,邮件服务器具有所有正确的 DNS 记录,并且服务器配置不违反任何主要黑名单系统的规则。在数百甚至数千条消息中,您编写应用程序所用的语言并不是主要因素。
【讨论】:
【参考方案6】:我在 php 中实现了一个批量邮件程序,其中每封电子邮件都是针对个人定制的。这并不难,也不需要太长时间。我使用了swiftmailer 和 cron。 Zend Mail 也可能没问题。我从 PEAR 邮件队列开始,但邮件排队太慢了。
排队邮件的过程是这样的:
-
创建电子邮件模板并为将替换独特内容的区域添加占位符(或使用模板引擎)。
在循环中,将占位符替换为任何唯一内容,将生成的电子邮件内容、主题、地址、批次 ID 和可选的优先级值插入到数据库表中。
我使用 cron 作业来发送批量电子邮件。 cron 时间间隔和每批发送的电子邮件数量很重要,因为我在一个有限制的共享主机上。 cron 作业调用的脚本只能由 cron 访问。该脚本从按批次 ID 排序的表中读取 x 封电子邮件,并且可以选择优先级。如果成功发送电子邮件,则将其从数据库队列中删除。如果无法发送电子邮件,它将保留在队列中,并为该记录增加一个计数器。如果计数器超过设定的数字,则电子邮件将从队列中删除。
【讨论】:
使用 PEAR_Mail 加速排队的几个有用技巧是在 Mysql 中将 *_seq 设为 MEMORY 表。我还将它包装在每 100 个条目的事务中【参考方案7】:Zend Mail 类看起来不错,使用简单,它还允许您发送纯文本和 HTML 版本的电子邮件,这在电子邮件营销中非常重要。
如果你熟悉框架,我会坚持下去。
向大量人员发送电子邮件时需要考虑的重要事项是:
您的网络服务器能否处理打开电子邮件时的图像请求 + 访问您网站的人的服务器负载。如果答案是否定的或您不确定,使用 apache benchmark 应该可以帮助您解决问题。如果您仍然不确定,最好批量发送电子邮件(可以使用 crontab 定时)以分散负载。
我希望这会有所帮助。
【讨论】:
这是一个令人困惑的答案,因为如果不使用 cron,那么在关注流量之前,发送电子邮件时的脚本超时将是一个问题。 我认为 Phil 建议您使用 cron 来限制电子邮件的发送。例如,一次只发送 100 个,每 30 分钟一次,直到列表用完为止。 但他似乎建议将 crontab 用作高流量的解决方案?无论如何,我们都应该幸运地通过营销活动产生过多的流量。很有可能,这不是问题。【参考方案8】:我用Swiftmailer 开发了一个时事通讯管理系统,它很容易实施。支持SMTP、加密、附件、批量发送、...
【讨论】:
以上是关于从 Zend Framework 应用程序向数百个收件人发送电子邮件的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Laminas项目(以前称为Zend框架)托管到Linux基金会
微软停止支持Windows 7 数百万台电脑将面临病毒等风险
微软停止支持Windows 7 数百万台电脑将面临病毒等风险