PHP-FPM与Nginx通信报 502 Bad Gateway或504 Gateway Timeout终极解决方案(适用于PHP执行耗时任务情况下的报错)

发布时间 2023-10-12 14:47:43作者: 小松聊PHP进阶

前置条件:

适用于常规请求都没问题,但是执行某些php脚本需要超过一分钟的情况下的502/504,并不是任何请求都502/504的情况(这说明php-fpm或者nginx配置错误)。

出现502/504的原因

502

执行脚本时间太长,期间php没有返回任何的数据。php-fpm超时,nginx没超时。nginx认为php-fpm罢工了,然后抛出了异常。

504

执行脚本时间太长,期间php没有返回任何的数据。php-fpm没超时,nginx超时。nginx认为php-fpm响应太慢,nginx没憋住抛出了异常。

不生效的解决方案(防止各位师傅踩坑):

代码

set_time_limit(0);
ignore_user_abort(true);
ini_set('max_execution_time', 600);

不生效原理剖析

以上代码的作用设置了php代码本身可以更长的时间处理任务并且不报致命错误,但不代表程序一定无限制的可以执行这么久。因为Nginx与PHP进程通信方式是检测到.php的文件交给php-fpm进程处理,php-fpm是一个fastcgi进程管理器,php-fpm一旦超时,php-fpm会强制终结掉这个进程,这就是报502的原因。
这段代码又无法控制nginx fastcgi的一些机制,所以报504的原因。
意味着仅代码层配置还不够,服务器也得配置。

官方文档对这2个函数和1个配置的解释:

##### set_time_limit:
Set the number of seconds a script is allowed to run. If this is reached, the script returns a fatal error. The default limit is 30 seconds or, if it exists, the max_execution_time value defined in the php.ini.

##### ignore_user_abort:
Set whether a client disconnect should abort script execution.When running PHP as a command line script, and the script's tty goes away without the script being terminated then the script will die the next time it tries to write anything, unless enable is set to true.

##### max_execution_time:
This sets the maximum time in seconds a script is allowed to run before it is terminated by the parser. This helps prevent poorly written scripts from tying up the server. The default setting is 30. When running PHP from the command line the default setting is 0.
On non Windows systems, the maximum execution time is not affected by system calls, stream operations etc. Please see the set_time_limit() function for more details.
**Your web server can have other timeout configurations that may also interrupt PHP execution..Apache has a Timeout directive and IIS has a CGI timeout function. Both default to 300 seconds. See your web server documentation for specific details.**
加粗字体意思是说:Web 服务器可以有其它超时配置,这些配置也可能会中断 PHP 执行。Apache 有一个 Timeout 指令,IIS 有一个 CGI 超时功能。 两者都默认为 300 秒。(nginx也有一个fastcgi超时配置,默认60秒)。

502解决方案

再php-fpm.conf中添加request_terminate_timeout = 600即可,如下:

#编辑php-fpm配置
vim /usr/local/php/etc/php-fpm.conf
#添加此配置,单位默认为秒,多少秒请根据情况自行设定
request_terminate_timeout = 600
#保存后重启
service php-fpm restart

504解决方案

再nginx配置中添加 fastcgi_connect_timeout 600; fastcgi_read_timeout 600; fastcgi_send_timeout 600; 即可,如下:

#编辑nginx某个站点的配置
vim /usr/local/nginx/conf/vhost/test.conf
#再location中添加以下配置,单位默认为秒,多少秒请根据情况自行设定,完整代码块如下:
location ~ \.php$ {
	fastcgi_pass   127.0.0.1:9000;
	fastcgi_index  index.php;
	fastcgi_param  SCRIPT_FILENAME $document_root/$fastcgi_script_name; 
	include        /usr/local/nginx/conf/fastcgi_params;
	fastcgi_connect_timeout 600; 
	fastcgi_read_timeout 600; 
	fastcgi_send_timeout 600; 
}

#保存后测试配置是否有问题,如果有问题,请修改好后再次尝试。
../../sbin/nginx -t
#确认配置正常,重启
service nginx restart

配置含义官方说明:

PHP-FPM:
request_terminate_timeout
The timeout for serving a single request after which the worker process will be killed. This option should be used when the 'max_execution_time' ini option does not stop script execution for some reason. A value of '0' means 'Off'. Available units: s(econds)(default), m(inutes), h(ours), or d(ays). Default value: 0.


Nginx:
fastcgi_connect_timeout 60s;
Defines a timeout for establishing a connection with a FastCGI server. It should be noted that this timeout cannot usually exceed 75 seconds.

fastcgi_read_timeout 60s;
Defines a timeout for reading a response from the FastCGI server. The timeout is set only between two successive read operations, not for the transmission of the whole response. If the FastCGI server does not transmit anything within this time, the connection is closed.

fastcgi_send_timeout 60s;
Sets a timeout for transmitting a request to the FastCGI server. The timeout is set only between two successive write operations, not for the transmission of the whole request. If the FastCGI server does not receive anything within this time, the connection is closed.