我的客户今天记录了一张票据,报告说当我尝试发送附件时,PHP的mail()
功能在我的一个Windows 2003 Server盒子上超时了。
经过调查,我能够重现他的问题。包含30-60Kb大小的小附件的消息需要15-20秒才能由mail()
函数处理。较大的附件大约360-500Kb所花费的时间超过允许的最大脚本执行时间(90秒)。
我能够在两台不同的Windows 2003服务器和一台Windows 2008R2服务器上重现该问题。我还尝试了三个不同版本的PHP(5.2.14,5.2.17和5.3.6 - 所有32位和所有非线程安全版本,根据Microsoft在Windows上运行PHP的建议)。
在所有情况下,邮件都是通过SMTP发送的(即不使用sendmail实现)。我尝试了三种不同的SMTP方案:
无论如何,发送附件仍然不是最理想的,这意味着问题无法固定在慢速继电器上。
然后我在CentOS服务器上运行了相同的代码,这些代码没有出现任何这些问题,mail()
函数几乎立即返回。但是,这些服务器上的PHP配置为使用sendmail
。
然后我决定使用PHP源代码来查找mail()
函数的实现情况,并在ext/standard/mail.c
中发现此代码:
if (!sendmail_path) {
#if (defined PHP_WIN32 || defined NETWARE)
/* handle old style win smtp sending */
if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, headers, subject, to, message, NULL, NULL, NULL TSRMLS_CC) == FAILURE) {
if (tsm_errmsg) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", tsm_errmsg);
efree(tsm_errmsg);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", GetSMErrorText(tsm_err));
}
return 0;
}
return 1;
#else
return 0;
#endif
TSendMail()
在另一个源文件(win32/sendmail.c
)中实现。最终,发送到SMTP服务器的所有数据似乎都通过Post()
中名为sendmail.c
的函数同步传递,如下所示:
static int Post(LPCSTR msg)
{
int len = strlen(msg);
int slen;
int index = 0;
while (len > 0) {
if ((slen = send(sc, msg + index, len, 0)) < 1)
return (FAILED_TO_SEND);
len -= slen;
index += slen;
}
return (SUCCESS);
}
send()
函数是winsock2
函数。
我想知道缓冲区大小(根据下面的知识库文章,默认为8K)或缺乏调优对于大量数据有一些影响。没有调用setsockopt()
指定缓冲区大小或任何其他选项来优化对send()
的调用。
使用SMTP传递的Windows上的mail()
功能可能不会用于发送大型电子邮件吗?
我有兴趣知道是否有其他人看过这段代码或经历过同样的事情。
Design issues - Sending small data segments over TCP with Winsock
为了清楚起见,我们现在已经为客户(SwiftMailer)提供了替代解决方案,因此这不是为了获得替代方案的建议。
答案 0 :(得分:1)
我的想法是在Windows上使用http://glob.com.au/sendmail/和PEAR Mail类:http://pear.php.net/package/Mail。这可能是您遇到的延迟的解决方法。我认为这将无法绕过缓冲区,但我认为值得一试。
另外,我从未使用它,但我听说过SwiftMailer的好消息:http://swiftmailer.org/