Nginx+php-fpm 模式下的502、504问题

客户端、nginx、php-fpm三者之间的关系

客户端发送请求给nginx,nginx将请求接收后,再发给php-fpm。nginx接收到php-fpm的响应后缓存到本地然后再发给客户端,或者一边缓存一边转发,这就是转发响应并以上游网速优先的模式。因为客户端和nginx之间是公网,网速慢环境差,nginx与php-fpm之间是内网,网速快且稳定。

什么是502、504

他们都表示nginx与其上游服务(这里是php-fpm)之间出了问题,502表示错误,504表示超时。下面都以php-fpm(或php,表示一个意思)为例。
502
表示php-fpm出现了错误、故障,有以下几种情况
1、tcp链接无法建立。这个时候,nginx会直接向客户端返回502错误,比如没有启动php-fpm进程、并发过高导致无可用php-fpm进程。
2、解析http协议时出现了问题。默认nginx使用一个4k的buffer缓存php-fpm的响应头(http头信息),如果php-fpm返回的响应头信息大于了4k,很明显,buffer装不下了。这时候nginx向客户端返回502。
3、php请求处理时间超时,php-fpm 子进程被master进程杀掉。(下面有说明)
504
nginx将客户端的请求完整转发给php后,会设置一个定时器,就是fastcgi_read_timeout,默认60秒,如果超过了60秒还没见PHP发回来响应的数据(包头或包体都算),那就给客户端返回504。如果在60秒内接收的到了响应数据,会重置定时器。

nginx用于代理的几个常见配置

fastcgi_connect_timeout 60;		//连接到PHP的超时时间(发起到建立成功,不是连接维持时间)
fastcgi_read_timeout 60;		//读取PHP响应的超时时间
fastcgi_send_timeout 60;		//发送请求数据的超时时间(没研究)
fastcgi_buffer_size 4k;			//用于接收响应头的缓冲(还有其他用处,这里不介绍)

fastcgi_connect_timeout 就是上面提到的tcp链接建立的超时时间
fastcgi_buffer_size 是上面提到的缓存大小
fastcgi_read_timeout 是上面504中提到的时间

下面说几个其他的问题

一个php-fpm同一时间能接受多少个请求?
PHP设置一个php-fpm进程,即 pm.max_children=1;
nginx采取上面的默认设置,超时时间都是60秒
test.php代码如下

$time = $_GET["time"];
if ($time) {
	sleep($time);
}
echo "OK";

浏览器打开两个窗口,依次执行下面的请求,分别返回什么
1、http://localhost/test.php?time=61
2、http://localhost/test.php?time=0
两个都是返回的504,问题是第二个为什么没有返回502 ?????
各位网友如果知道原因,请不吝赐教,评论告诉我。(已排除文件session导致的文件锁问题,并且只开了一个进程)

并发较大时nginx日志中的499问题
499 表示客户端主动断开连接
如上面的测试,浏览器执行 http://localhost/test.php?time=61,然后关闭请求,nginx中就会记录499。大并发下由于响应比较慢,用户会不停的刷新,关闭请求。

大并发下返回的错误是502还是504
答案是500,502,504都有。
500可能是系统资源不足导致,如文件打开句柄数不够,可进行如下nginx设置
worker_rlimit_nofile 65535;(或直接设置Linux系统的)
502和504的原因没有搞清楚。

补充几个PHP知识点

1、set_time_limit()函数和php.ini中的 max_execution_time
指的是PHP脚本执行的最大时间,单位秒。可以理解为最多占用CPU的时间,像sleep()、file_get_contents()、操作MySQL这种等待IO的时间是不算在内的。超过执行时间后返回的是Fatal error,状态码是200,不是502。php-fpm 进程也会被杀死。

Fatal error: Maximum execution time of xx seconds exceeded in
ngix有类似如下的错误日志信息
FastCGI sent in stderr: "PHP message: PHP Fatal error:  Maximum execution time of 1 second exceeded in /Users/mywork/wwwroot/test.php on line 13" while reading response header from upstream

2、php-fpm的配置:request_terminal_timeout
指请求处理的总时间,包括 sleep() 等在内的总时间。超过这个时间后,请求会直接被关闭,php-fpm子进程被master进程杀死。nginx返回502,nginx错误日志会有如下错误

kevent() reported about an closed connection (54: Connection reset by peer) while reading response header from upstream

3、大并发下的session
php.ini 中的 session.auto_start = 0。如果做api开发,且没有用到PHP的session机制,建议关闭session自动开启。session如果设置的是文件存储,每次请求都会操作文件,对文件上锁,获取不到锁就会等待。所以在一个php-fpm进程中 sleep(),所有进程都会等着。


版权声明:本文为u010320108原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。