当前位置:首页 > 运维 > 正文内容

彻底弄懂502/503/504(php-fpm+nginx)亲测可用

phpmianshi6年前 (2015-04-16)运维707

环境 php7.3.5 + nginx1.16.0


相信大家都遇到过50X的问题,网上也看了很多文章,总是各种不对,所以今天咱们详解各种出现50X的情况和原因

502:Bad Gateway  作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。

503:Service Unavailable 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个 Retry-After 头用以标明这个延迟时间。如果没有给出这个 Retry-After 信息,那么客户端应当以处理500响应的方式处理它。

504:Gateway Time-out 作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。

499:client has closed connection 客户端(ab压测那端)等待不耐烦,关闭自身致使连接从客户端先断开,nginx检查到客户端已断开连接,则报499 code 。(注:其他情况如用户主动关闭浏览器等)


nginx+php 出现502 bad gateway,一般这都不是nginx的问题,而是由于 fastcgi或者php的问题导致的,常见的有以下几种。

1.php-fpm进程挂掉或者重启,大家可以service php-fpm stop 然后再打开php页面就返回502

nginx错误日志:

*153514 connect() to unix:/dev/shm/php-cgi.sock failed (2: No such file or directory) while connecting to upstream

所以平时我们要平滑重启 kill -USR2 pid  就不会报错了


2.php-fpm 平滑重启时也是有可能有502的,大家可以打开一个sleep(10) 的页面,然后service php-fpm reload  或者 kill -USR2 pid 测试

php-fpm错误日志:

/phpmianshi.com/test.php' (request: "GET /test.php") executing too slow

主要原因是:php-fpm.conf中process_control_timeout 设置过小造成的(默认0),sleep 收到 reload 发出的信号后直接返回了,所以出现了502

process_control_timeout 参数解释

参数含义是 设置子进程接受主进程复用信号的超时时间. 控制子进程处理来自master的信号的时间,默认为0.如果正在处理请求, 很可能会收到错误报警。


网上有专家介绍:建议将此参数设置为相同的值 request_terminate_timeout,以便worker有时间完成处理请求, 否则将会中断。


但是实际情况并不乐观,当设置过大时,压测会发现大量的502返回


我们改写一下测试脚本,设置 process_control_timeout = 5s

<?php
    echo 1;sleep(5);
    echo 2;sleep(5);
    echo 3;
?>

为了方便观察php-fpm进程数的变化,我们设置php-fpm.conf

pm = static
pm.max_children = 20

reload后设置监控:

watch -n 1 'ps aux |grep php-fpm'

这时候我们打开测试脚本 https://phpmianshi.com/test.php  接着执行 service php-fpm reload

查看监控页面发现,除了一个进程正在执行该脚本外,其他进程全部被kill掉了,一直到该脚本执行完毕,才正在的reload成功,启动了5个php-fpm

虽然reload导致第一个sleep立刻返回了,但是第二个sleep没有收到reload信号,所以超时时间大于了process_control_timeout的值,于是又返回了502

总结:process_control_timeout设置一个合理的值可以做到真正的平滑重启,但是也不能设置过大,设置过大reload过程中,如果有比较慢的处理,其他进程都没有启动,在高并发场景下会有更大的阻塞


3.request_terminate_timeout 设置的过小,php没有执行完就被中断,大家可以设置小一点,写一个sleep脚本测试

php-fpm错误日志:

[18-May-2015 19:37:47] WARNING: [pool www] child 7906, script '/data/wwwroot/mianshiphp/test.php' (request: "GET /test.php") executing too slow (1.295579 sec), logging
[18-May-2015 19:37:47] WARNING: [pool www] child 7906, script '/data/wwwroot/mianshiphp/test.php' (request: "GET /test.php") execution timed out (1.629247 sec), terminating
[18-May-2015 19:37:47] WARNING: [pool www] child 7906 exited on signal 15 (SIGTERM) after 72.682120 seconds from start

4.nginx fastcgi_read_timeout 设置过小,返回504

比如:设置 fastcgi_read_timeout =1 ,php脚本sleep(3)  ,则返回504

php-fpm错误日志:

[18-May-2020 20:01:47] WARNING: [pool www] child 12044, script '/data/wwwroot/mianshiphp/test.php' (request: "GET /test.php") executing too slow (1.235212 sec), logging

5.当服务器压力过大,没有更多的php-fpm处理请求时,返回504

比如设置php-fpm进程数为1,压力测试 ab -n 100 -c 20 https://phpmianshi.com/?id=90


有文章说,压力过大,没有足够的php-fpm处理时会返回502,这里还是分多种情况,我这边测试部分请求返回了200,其他请求返回了504


说明当php-fpm不足时,是有一个等待队列存在的。已经接到请求的php-fpm会返回200,长时间得不到php-fpm处理的请求就返回了504,


那么这个等待队列是什么呢?分析如下:


其实就是php-fpm.conf 中的listen.backlog配置 ,当backlog队列满了,会出现502错误

nginx_errror.log显示如下:

[error] 7820#0: *157186072 connect() to unix:/tmp/php-cgi.so
ck failed (11: Resource temporarily unavailable) while connecting to upstream



首先查看php活跃的套接字:  ss -ln |grep -E 'php|Netid'


Netid  State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
u_str  LISTEN     0      32768  /dev/shm/php-cgi.sock 79562794              * 0


关注 Recv-QSend-Q 这两个字段。


LISTEN 状态: Recv-Q 表示的当前等待服务端调用 accept 完成三次握手的 listen backlog 数值,也就是说,当客户端通过 connect() 去连接正在 listen() 的服务端时,这些连接会一直处于这个 queue 里面直到被服务端 accept();Send-Q 表示的则是最大的 listen backlog 数值,这就就是上面提到的 min(backlog, somaxconn) 的值。


于是修改listen.backlog = 1 ,同时开ab -n 5 -c 5 https://phpmianshi.com/backlog.php 测试,发现所有请求又都返回200了


ss -ln |grep -E 'php|Netid'


Netid  State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
u_str  LISTEN     3      32768  /dev/shm/php-cgi.sock 79562794              * 0


过一会 Recv-Q 慢慢减少到0,所有请求执行完成,说明php-fpm并没有拒绝后两次请求


具体原因如下:


当 queue 满了之后,服务器并不会按照理论所述,不再对 SYN 进行应答,返回 ETIMEDOUT。根据这篇文档的描述,服务器会随机的忽略收到的 SYN,建立起来的连接数可以无限的增加,只不过客户端会遇到延时以及超时的情况。


总结:适当增加max_children还是有用的,这样的话php-fpm能同时处理的请求增加,客户端的延迟等待时间也会相应的减小。


6.nginx配置了频率限制而client端又超过了配置的限制后就会收到503的响应。

nginx错误日志:

2015/05/21 03:29:02 [error] 23794#0: *525984942 limiting requests, excess: 20.66
0 by zone "one", client: 39.97.180.224, server: www.phpmianshi.com, request: "GET
/api/live.php HTTP/1.1", host: "www.phpmianshi.com", referrer: "-"


7.nginx client_max_body_size 128m; 设置过小,返回504

比如:设置 client_max_body_size 64m; 当上传文件的大小超过64m  ,则返回504

nginx错误日志:

2021/04/19 18:29:04 [error] 12872#0: *9110961 client intended to send too large body: 120730354 bytes

其他待补充...


总结:


解决问题的最好的方式还是自己去看nginx和fastcgi的errorlog。

最后做个总结: php-cgi进程数不够用、php执行时间长、或者是php-cgi进程死掉,都会出现502错误。

当nginx收到了无法理解的响应时,就返回502。当nginx超过自己配置的超时时间还没有收到请求时,就返回504错误。







版权声明:本文由PHP面试资料网发布,如需转载请注明出处。
分享给朋友:

相关文章

linux中配置内核参数sysctl详解

概念sysctl用于运行时配置内核参数,这些参数位于/proc/sys目录下。sysctl配置与显示在/proc/sys目录中的内核参数。用户只需要编辑/etc/sysctl.conf文件,即可手工或...

记一次连接Redis偶现超时的问题

记一次连接Redis偶现超时的问题

问题描述公司老的项目没有任何监控,对于系统的运行健康情况完全不知,于是搭建了2套监控系统,一套sentry监控代码层面的exception,一套cls告警,监控所有系统的状态码,应用日志等。监控系统上...






TCP(Transmission Control Protocol) 传输控制协议

三次握手

TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:

位码即tcp标志位,有6种标示:

SYN(synchronous建立联机) 同步报文段

ACK(acknowledgement 确认)

PSH(push传送)

FIN(finish结束) 结束报文段

RST(reset重置) 复位报文段

URG(urgent紧急) 紧急指针

Sequence number(顺序号码)

Acknowledge number(确认号码)

客户端TCP状态迁移:

CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED

服务器TCP状态迁移:

CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED


各个状态的意义如下: 

LISTEN - 侦听来自远方TCP端口的连接请求; 

SYN-SENT -在发送连接请求后等待匹配的连接请求; 

SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认; 

ESTABLISHED- 代表一个打开的连接,数据可以传送给用户; 

FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;

FIN-WAIT-2 - 从远程TCP等待连接中断请求; 

CLOSE-WAIT - 等待从本地用户发来的连接中断请求; 

CLOSING -等待远程TCP对连接中断的确认; 

LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认; 

TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认; 

CLOSED - 没有任何连接状态;


TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接,如图1所示。

(1)第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。

(2)第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。

(3)第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。

确认号:其数值等于发送方的发送序号 +1(即接收方期望接收的下一个序列号)。

图1 TCP三次握手建立连接  


TCP协议中的三次握手和四次挥手

理解:窗口和滑动窗口TCP的流量控制TCP使用窗口机制进行流量控制什么是窗口?连接建立时,各端分配一块缓冲区用来存储接收的数据,并将缓冲区的尺寸发送给另一端接收方发送的确认信息中包含了自己剩余的缓冲区...

linux中set指令用法

简介我们知道,Bash 执行脚本的时候,会创建一个新的 Shell,这个 Shell 就是脚本的执行环境,Bash 默认给定了这个环境的各种参数。set命令用来修改 Shell 环境的运行参数,也...

linux下utf-8 BOM的检查和删除

背景当源程序是gbk格式,你转换为 utf8 的时候,很多情况是头部会出现bom,当是php 程序时候,这样会出现很多意想不到的事情,那怎么办呢,你可以用linux 命令来查找,然后对文件的bom 进...

记一次laravel项目因opcache导致的include过慢问题

问题表现 php-fpm-slow.log 大量如下日志:script_filename = /data/nginx/webroot/app-20200611-160330-feb...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。