今天客户提交了一个工单,报告我们的 Windows 2003 服务器之一在尝试发送附件时 PHP 的
经过调查,我成功重现了他的问题。包含大小约为 30-60KB 的小附件的邮件需要 15-20 秒才能被
我在两台不同的 Windows 2003 服务器和一台 Windows 2008R2 服务器上都能够重现这个问题。我还尝试了三个不同版本的 PHP(5.2.14、5.2.17 和 5.3.6 - 均为 Microsoft 建议在 Windows 上运行 PHP 的非线程安全构建版本),但问题依然存在。
在所有情况下,邮件都是通过 SMTP 发送的(即未使用 sendmail 实现)。我尝试了三种不同的 SMTP 场景:
然后我在我们的 CentOS 服务器上运行了相同的代码,但没有出现任何这些问题,
接着我决定深入研究 PHP 源代码以查找
我在想,对于更大量的数据,缓冲区大小(根据下面的KB文章,默认值为8K)或者缺乏调整是否会产生影响。没有调用
也许在Windows上,使用SMTP传递的
我很想知道是否有人已经查看过这段代码或者遇到了同样的问题。
mail()
函数超时了。经过调查,我成功重现了他的问题。包含大小约为 30-60KB 的小附件的邮件需要 15-20 秒才能被
mail()
函数处理;大小约为 360-500KB 的大附件则需要更长时间,甚至超出了脚本执行时间的最大限制(90 秒)。我在两台不同的 Windows 2003 服务器和一台 Windows 2008R2 服务器上都能够重现这个问题。我还尝试了三个不同版本的 PHP(5.2.14、5.2.17 和 5.3.6 - 均为 Microsoft 建议在 Windows 上运行 PHP 的非线程安全构建版本),但问题依然存在。
在所有情况下,邮件都是通过 SMTP 发送的(即未使用 sendmail 实现)。我尝试了三种不同的 SMTP 场景:
- 直接传递到我们的 SMTP 智能主机集群(运行 exim)
- 通过本地 IIS SMTP 服务中继到我们的智能主机
- 通过本地 IIS SMTP 服务进行 MX 查找和直接投递
然后我在我们的 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
函数。我在想,对于更大量的数据,缓冲区大小(根据下面的KB文章,默认值为8K)或者缺乏调整是否会产生影响。没有调用
setsockopt()
来指定缓冲区大小或任何其他优化调用send()
的选项。也许在Windows上,使用SMTP传递的
mail()
函数不适合用于发送大型电子邮件?我很想知道是否有人已经查看过这段代码或者遇到了同样的问题。
只是为了明确,我们已经为客户提供了备选解决方案(SwiftMailer),因此这并不是为了得到替代建议。