微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

nginx+php-fpm服务HTTP状态码502怎么解决

这篇文章主要介绍“Nginx+PHP-fpm服务HTTP状态码502怎么解决”,在日常操作中,相信很多人在Nginx+PHP-fpm服务HTTP状态码502怎么解决问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Nginx+PHP-fpm服务HTTP状态码502怎么解决”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

我们的一个web项目,由于新上城市增多,导致访问量增大,db压力增大,作为提供接口的业务方,最近被下游反馈大量请求“502”。

502,bad gateway,一般都是upstream(这里就是PHP)出错,对于PHP,造成502的原因常见的就是脚本执行超过timeout设置时间,或者timeout设置过大,导致PHP进程长时间不能被释放,没有空闲worker进程来接客。

我们的项目就是PHP执行时间设置过短导致的,对于这种情况,可以先适当增大PHP的执行时间,先保证清除502,优化的事情毕竟要花更多的时间。

控制PHP执行时间的选项有两个,在PHP.ini中 max_execution_time 和PHP-fpm中 request_terminate_timeout,其中 request_terminate_timeout 可以覆盖 max_execution_time,所以如果不想改全局的PHP.ini,那只改PHP-fpm的配置就可以了。

下边我就来详细的分析一下为什么PHP脚本执行超出设置时间会导致Nginx返回502

先来布景,让问题复现:

NginxPHP分别只启动一个worker,方便追踪。

PHP-fpm的request_terminate_timeout设置为3s。

测试脚本test.PHP

sleep(20);
echo 'ok';

go go go:

在浏览器访问www.v.com/test.PHP,3s后如期出现...404???what???

nginx+php-fpm服务HTTP状态码502怎么解决

出师不利啊,赶紧看看Nginx配置文件

nginx+php-fpm服务HTTP状态码502怎么解决

这个location配置是当发生5xx错误跳转一个好看点的界面,但是我在/usr/share/Nginx/html下并没有50x.html这个文件。所以搞了个404出来。这不是很影响我判断问题的准确性?直接注释掉!再次访问,等待3s,终于'正常'的界面出来了。

nginx+php-fpm服务HTTP状态码502怎么解决

环境好了,下边就上套路,按照web问题的排查套路走一遍,先看看错误日志吧:

Nginx:

nginx+php-fpm服务HTTP状态码502怎么解决

报错都是 recv() Failed (104: connection reset by peer。

recv时失败了,连接被重置了。为啥连接被重置了?难道一言不合。

我们在看看PHP-fpm的错误日志:

(注意PHP-fpm中PHP_admin_value[error_log]选项指定PHP错误日志,会覆盖PHP.ini中的。但是这里不是看PHP错误,而是看PHP-fpm的错误PHP-fpm的错误日志由PHP-fpm.conf中的error_log选项指定。)

nginx+php-fpm服务HTTP状态码502怎么解决

每一次请求都是产生2个warning和1个notice:

warning:脚本执行超时了,终止了。

warning:子进程收到sigterm信号退出了。

notice:启了一个新的子进程(因为我设置的pm.min_spare_servers = 1)

看来如果PHP的worker进程执行超时,不仅终止脚本执行,而且worker进程也会退出。看来Nginx的报错连接被重置是因为PHP的worker进程退出了(在tcp连接中一方如果断掉的话会发送rst给另一方)

通过日志已经可以知道PHP脚本执行超时,worker子进程退出,导致Nginx报错connection reset by peer,下边我们通过strace来看看PHPNginx的情况:

PHP

nginx+php-fpm服务HTTP状态码502怎么解决

1.accept一个Nginx的连接请求(socket,bind,listen都在master中完成 ),可以看到Nginx的端口是47039,从fd0中读取数据,就是从标准输入中,这个是fast-cgi协议规定的。accept之后的已连接描述符是3。

2.从fd3中读取Nginx传递过来的数据,fastcgi协议格式,接收了856字节。为什么read5次呢?

因为fastcgi协议数据包是8字节对齐,由包头和包体组成。并且都是会先发一个request数据包,包含一些请求id,版本,typpe等信息(包头包体各占8字节),再发一个params数据包,传递get参数和环境变量(包头8字节,包体变长),最后发送一个没有包体只有包头的params数据包,表示参数发送结束(包头8字节)。所以前3个read用来读出request包的包头和包体,还有params包的包头,第四个read是读取真正的数据,最后一个read是读取最后一个params包的包头。所以Nginx传递的数据应该是8+8+8+856+8=896字节(和下边Nginx的传输bytes能对应上)。注意如果是post方式,还会发送stdin数据包。

3.设置休眠20s,就是PHP程序中的sleep(20),之后由于进程被终止了,所以后边就没啦。strace程序也退出啦。

Nginx

nginx+php-fpm服务HTTP状态码502怎么解决

1.accept到浏览器的请求,可以看到浏览器端的端口是56434,ip是192.168.1.105,已建立连接的fd是3。

2.从fd3中接收数据,http协议。

3.创建一个socket,fd21,用于和PHP建立连接。

4.连接到fd21,可以看到连接的是本机的9000端口,这里NginxPHP-fpm使用ip socket连接方式,NginxPHP-fpm部署在一台机器上可以考虑unix domain socket。

5.向fd21写入数据,fast-cgi协议格式,我们看到写入的长度是896,和上边的PHP接收的长度是对应的。

6.recvfrom函数从fd21中返回 econnreset (connection reset by peer)

7.向fd9中写入错误信息,可以推断fd9就是Nginx错误日志的文件描述符。

8.关闭和fd21的连接。

9.向fd3写入502 bad gateway,就是返回给浏览器的信息。

10.向fd8写入一条访问日志,可以推断fd8就是Nginx访问日志的文件描述符。

来验证一下Nginx访问日志和错误日志的推断。可以看到的确是fd8,fd9,并处于写入模式。

nginx+php-fpm服务HTTP状态码502怎么解决

那么在这个过程中整个网络包的传输我们不妨也看一下:

通过tcpdump抓包,用神器看比较方便。

因为只想看NginxPHP的通讯,在上边又知道Nginx的端口是47039,可以通过tcp.srcport==47039过滤出对应的包。

nginx+php-fpm服务HTTP状态码502怎么解决

可以看到NginxPHP-fpm数据交互的过程:47039->9000建立三次握手,接着向9000发送数据,9000回复ack,3s后9000回复rst。没毛病。

注意:

syn,fin各占一个序列号

ack,rst不占序列号(28,29两个包的reqnum和acknum都是相同的)

序列号是每一字节加1(29包发送896字节,同时29包seq为4219146879,30包的ack为4219147775,正好相差896)

rst不需要回复

到此,关于“Nginx+PHP-fpm服务HTTP状态码502怎么解决”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程之家网站,小编会继续努力为大家带来更多实用的文章

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐