同样的代码同样的邮箱账号在本地测试(lnmp1.5)通过,在腾讯云线上环境则出现一直加载直到超时。中途遇到若干可能出现问题的检测点,最终解决备忘如下。

环境:

LNMP 1.4 (一键部署)
PHP Version 7.1.7
PHPMailer 5.2.22
邮箱账号 126.com

可能的问题#1 : 126邮箱账号设置

账号是126.com新注册的,专门用作发送邮件通知用。也检查了并不是邮件发送出去被当作垃圾邮件拦截的问题(如果遇到这个问题可以在收件方添加白名单方式解决)。
会造成问题的原因是新注册账号 SMTP/POP/IMAP服务默认是关闭的,当在网页版邮箱设置开启服务的时候要求验证并设置一个第三方授权码的东西才可以开启,而且重点是使用SMTP时(包括其他协议),SMTP的认证用密码是这个第三方授权码而不是邮箱的(网页)注册、登录密码
QQ截图20190607010530.jpg

可能存在的问题#2: LNMP4 中的PHP默认编译文件存在问题

发现lnmp的社区里的一则讨论和我的情况很相似,是这个问题导致的可能性最大。刚好这位网友用的和我相同的版本,而且他又引用了另一篇文章的解决办法,重新编译 PHP-FPM。考虑到我两个环境测试的差异主要的区别是 LNMP版本不同,认为这个可能性很大。但由于线上涉及的业务较多,贸然升级或者重编译会有较大的风险,所以不到最后不考虑这个方向解决问题。但确实也从社区的讨论里得到启发寻获了解决问题的方法。也就是下一点。

可能存在的问题#3:尝试修改phpMailer的发信模式为SSL (默认为TLS)

在上文的排查过程中,依次已经做了以下步骤寻找线索

  1. php_errorlog 这一步没有的发现,由于是执行到超时并不是遇到错误,所以错误日志并没有记录任何问题
  2. 打开php-fpm的慢日志

vi /usr/local/php/etc/php-fpm.conf

;request_slowlog_timeout = 0
request_slowlog_timeout = 2
;slowlog = var/log/slow.log
slowlog = /home/wwwlogs/php_slow.log

修改上述内容,把request_slowlog_timeout值设为2, 执行超过2秒的脚本记录在慢日志中,然后重启 php-fpm,追踪慢日志

lnmp php-fpm restart
tail -f /home/wwwlogs/php_slow.log

再次运行脚本,从慢日志上看和上文lnmp社区的输出一致

[07-Jun-2019 00:01:16]  [pool www] pid 30568
script_filename = /wwwroot/path/cronjobs.php
[0x00007fd9d1816420] stream_socket_client() /wwwroot/path/class/mailer/class.smtp.php:293
[0x00007fd9d1815f40] connect() /wwwroot/path/class/mailer/class.phpmailer.php:1660
[0x00007fd9d18156e0] smtpConnect() /wwwroot/path/class/mailer/class.phpmailer.php:1540
[0x00007fd9d18150b0] smtpSend() /wwwroot/path/class/mailer/class.phpmailer.php:1335
[0x00007fd9d1814e90] postSend() /wwwroot/path/class/mailer/class.phpmailer.php:1213
[0x00007fd9d1814dc0] send() /wwwroot/path/inc.conf.php:164
[0x00007fd9d1814a70] mailto() /wwwroot/path/cronjobs.php:141

检查phpinfo() ,确认 stream_socket_client() 不在 disable_functions中(如果在,可以尝试修改php.ini后重启php尝试)
QQ截图20190607013059.jpg

依据最初得到的 phpmailer的错误提示

SMTP connect() failed

顺藤摸瓜到 phpMailer的官方github issues, 得到一个大佬的提示

You could try setting $mail->SMTPDebug = 4; to get more feedback on connection errors. ...

<?php
define("MAIL_SMTP","smtp.126.com"); //smtp服务器地址
...

$mail = new PHPMailer();
$mail->Host =  MAIL_SMTP;
$mail->SMTPDebug = 4;//debug
...
$mail->Send()
?>

依照方法增加debug信息后,得到两处提示

535 Authentication failed

535错误提示让我找到了126邮箱应该使用第三方授权码作为smtp密码的问题。
另一处提示为phpmailer在默认情况下尝试与126邮件服务器作 TLS握手

QQ截图20190607014102.jpg

根据社区的讨论,改为请求SSL会如何呢
于是修改 邮件服务器的链接相关PHP代码

<?php
define("MAIL_SMTP","ssl://smtp.126.com:465"); //smtp服务器地址
...

$mail = new PHPMailer();
$mail->Host =  MAIL_SMTP;
$mail->SMTPDebug = 4;//debug
$mail->set('SMTPSecure', 'ssl');
...
$mail->Send()
?>

邮件秒发出,看来改用SSL可以避免问题发生

由此推论,lnmp1.4的 PHP源码确实存在缺陷在特定的环境或编译时会导致phpmailer 用默认方式用126的邮件服务发信出现502超时的问题。这种问题也许仅存在于同时满足特定的3到4项条件时才会触发,所以也许不同版本的操作系统、其他的邮件服务商、其他版本的phpmailer或许并不会有问题。
这篇备忘或者并不能通用解决每一例同样表象的 phpmailer发信失败的问题。但也费了我大半天时间,谨作备忘。

补充
网上普遍的其他的解释,说可能是 php-fpm池连接数达到上限等,这些简单检查过已经排除并不是


参考文
php502 gateway error 解决办法 http://www.nginx.cn/102.html
日志-php-fpm慢日志查询 https://blog.csdn.net/ty_hf/article/details/55504172
lnmp社区 https://bbs.vpser.net/thread-16051-1-1.html
重编译php-fpm http://www.linuxdiyf.com/linux/26343.html
phpmailer github https://github.com/PHPMailer/PHPMailer/issues/270

标签: php, lnmp, phpmailer

添加新评论