星点互联edus视频培训演示站

标题: PHP进程卡死和MySQL超时时间的设置方法 [打印本页]

作者: 超级玛丽    时间: 2020-12-1 09:15
标题: PHP进程卡死和MySQL超时时间的设置方法
最近线上一台服务器的nginx总是会有一部分请求(不是所有请求)报upstream timed out (110: Connection timed out) while connecting to upstream的错误,看起来像是后端的phpcgi进程出问题了,但如果phpcgi进程有问题,不是应该所有请求都会报错才对么,于是展开排查。
排查原因
在我们服务器上,PHP是使用9006端口进行监听的,执行netstat -an | grep 9006命令查看相关连接的网络状态,看到有一部分连接处于CLOSE_WAIT状态:

  1. tcp        0      0 10.0.0.188:9006          10.0.0.52:37316         CLOSE_WAIT  
  2. tcp        0      0 10.0.0.188:9006          10.0.0.52:37292         CLOSE_WAIT  
  3. tcp        0      0 10.0.0.188:9006          10.0.0.52:37300         CLOSE_WAIT  
  4. tcp        0      0 10.0.0.188:9006          10.0.0.52:37302         CLOSE_WAIT  
  5. tcp        0      0 10.0.0.188:9006          10.0.0.52:37234         CLOSE_WAIT
复制代码

奇怪的是,这些连接一直停留在CLOSE_WAIT的状态,不会变化,所以应该就是这些连接导致PHP进程被卡死,无法处理新的请求,从而导致部分请求报Connection timed out错误的。
根据TCP连接的四次挥手过程,CLOSE_WAIT状态是连接被关闭方才会出现的。nginx调用PHP,PHP响应太慢导致nginx超时,继而nginx主动关闭跟PHP的TCP连接,因此PHP进程是被关闭方,这个没啥问题。但PHP进程在收到nginx的关闭请求后,应该也跟着关闭才对,但实际没有,而是停留在了CLOSE_WAIT状态,说明PHP被某些东西卡住了,没办法关闭连接。
于是使用strace -p PHP进程ID查看PHP卡在了什么地方,等了一两分钟,strace命令什么东西都没输出,说明PHP进程是卡在了某个系统调用:
猜想会不会是mysql导致PHP卡死呢,于是根据显示的连接端口号10465,到mysql服务器上查看这个端口的连接情况,发现居然没有这个端口的TCP连接。这就神奇了,这里我只能猜测是:PHP成功跟mysql服务器建立TCP连接,但由于丢包或者防火墙拦截等奇怪的原因,PHP没有收到mysql的greeting packet,于是导致PHP一直在这里空等。后面mysql服务器主动断开TCP连接,但发送的FIN包也被拦截了,导致收不到PHP的ACK回应,于是mysql继续释放了这个连接。于是就出现了这个连接在PHP端是ESTABLISHED状态,但在mysql端却不存在这个连接的情况。
确认和模拟测试
为了确认到底是不是mysql连接卡死了PHP,使用gdb -p PHP进程ID进行调试,上面lsof命令显示mysql的连接对应的文件描述符是5u,gdb里输入命令call close(5)强制把这个连接关闭,关闭后PHP进程的CLOSE_WAIT状态马上消失了,证明确实是这个连接卡住了PHP。
接着我尝试模拟“成功建立TCP连接,但收不到mysql greeting packet”的这种情景,看PHP会不会卡死,测试代码如下:

  1. $mysql = new mysqli();
  2. $mysql->real_connect('45.113.192.102', 'root', 'xxx', 'xxx', 80);
复制代码







作者: 天才儿童Caya    时间: 2020-12-1 09:38
学习了
作者: admin    时间: 2020-12-1 09:39
方法不错啊
作者: 超级玛丽    时间: 2020-12-1 10:36
谢谢捧场啊




欢迎光临 星点互联edus视频培训演示站 (http://47.100.112.22/demo/edus/) Powered by Discuz! X3.5