背景

上周都是查测试环境的问题,比如,我上一篇写的问题排查:nginx的反向代理感觉失效了一样 ,就是说这个事的。在文章里,最终查到是nginx的全连接队列满了(每个监听端口有个队列,完成三次握手的请求会进入这个监听端口的全连接队列,队列大小是只有128,比较小),我当时的解决方式,是把队列大小调大到了512,然后重启nginx,果然功能正常了。

但当时没有去再多问一个为什么:为什么nginx的全连接队列会满呢?而且这个功能虽然用得少,但是之前应该都好好的,突然就抽风了?

nginx的全连接队列,简单,它就是一个队列,网络协议栈负责将完成三次握手的连接放到该队列,算是生产者;那么,消费者是谁呢,自然是我们的应用程序,应用程序每次调用accept的时候,就会从队列中获取一个连接,获取了之后,队列也就空出来了。

我翻了下accept这个syscall的手册(man accept),https://linux.die.net/man/2/accept:

这块是说,是从队列头部获取,先进先出;获取到了之后,给调用方返回一个文件描述符,指向该socket。

这里又提到,如果队列里没有连接,且该监听socket不是非阻塞的,那么accpet调用会阻塞;如果socket是非阻塞的,那么此时就会返回错误:EAGAIN or EWOULDBLOCK。

为了在新的连接到达时(进入队列时)能够得到提醒,我们可以使用select或者poll机制。当新连接到达时,会有一个可读事件发送给程序,此时再去调用accept就肯定能获取到连接,而不会阻塞。

我发现,文档写得还是非常清楚,很有价值,总的来说,应用程序就是那个消费者,队列会满,那肯定是消费者有问题,消费者是nginx,nginx能有啥问题呢,还真不知道,我当时以为猜测可能是请求处理有点慢吧,我把队列给你加大了,应该就没事了。

当天下午有事,也没管了,结果第二天,测试同事说,又不行了,我去看了下队列(ss -lnt|grep 8088),512的队列,又满了。

排查

nginx 日志

说实话,当时真的有点无语,因为手里还有别的事,也不想一直耗在这个事情上,但是,我们也不能阻碍测试同事工作开展,这也是份内事。

因为当时这个nginx,除了监听了我们关注的8088端口,还监听了8082 8080等端口,我以为是被其他端口影响到了,也看了下这两个端口的队列长度,ss -lnt|grep 8082,发现队列长度是0,只有我们8088端口是满的。

我当时想过,怎么看看这些队列里都是啥内容,但感觉意义不大,无非就是那些socket,所以没有深入去看。

然后又去看nginx日志,首先奇怪的一点是,我是周四的下午2点多处理完队列长度,重启nginx,然后access日志也只打到了2点20分左右就没了,error日志也是。

另外,error日志倒是打印了一些内容,就是有很多获取图片的请求,处理失败了,就是说open xxx.jpg failed之类的,我当时想着估计是图片找不到吧,404啥的,这种见多了,一般也不怎么理。

我还是去看了下那个文件,结果,ssh卡死了:

[root@168assi log]# ll /hxspace/production/files/unsafty/hxtg_dd
^C^C^C^C

我之前就是感觉这个机器有点问题,经常各种命令都卡死,现在ll都不行了。。

常规检查

然后就是开始检查系统资源,首先是top,按cpu排序和按内存排序,都没发现很离谱的占用很高的应用。

但是,top中看到1、5/15分钟的平均负载基本在12左右,我们是8核,按我理解,12/8=1.5,那基本上,每个核上有1个线程在运行,还有0.5个线程在等待运行。

感觉是有点高,但我不太擅长排查cpu问题,也没有再深入。

然后free -h检查了下内存,空闲内存也还很多,8个g,感觉没问题。

然后是磁盘,df一执行,结果直接卡死了,也不知道咋回事,之前就是感觉这机器有问题,之前lsof命令也是执行卡死。

当时都怀疑是不是磁盘有问题,还是机器哪里有问题,要不要换台机器部署算了。

内核日志

再检查下内核日志吧,dmesg -T,浏览了下。没看到nginx的相关日志。但是看到一些乱七八糟的内容:

[五 8月 25 11:06:58 2023] nfs: server 10.0.251.111 not responding, timed out
[五 8月 25 11:06:58 2023] nfs: server 10.0.251.111 not responding, timed out
[五 8月 25 11:12:04 2023] nfs: server 10.0.251.111 not responding, still trying

然后又去看了/var/log/messages,也没啥特别的。

strace查看df阻塞点

之前不是执行df,把我ssh卡死了嘛,我这次想看看到底是哪里卡住了,于是用strace跟踪了下。

[root@168assi servers]# strace -q -f -s 500 df

结果,这个命令卡在了下面这个地方:

stat("/run/user/42", {st_mode=S_IFDIR|0700, st_size=180, ...}) = 0
stat("/sys/fs/fuse/connections", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
stat("/run/user/42/gvfs", 0x7ffe818bf1a0) = -1 EACCES (Permission denied)
stat("/run/user/0", {st_mode=S_IFDIR|0700, st_size=40, ...}) = 0
stat("/hxspace/production/files/unsafty/hxtg_dd", ^C^C^C^C

可以看到我狂按ctrl c吗,就是因为卡死了

检查挂载

这个目录有问题啊,因为这个目录我还是很有印象的,nginx读图片就是在这个目录下读,然后还失败了。

然后我想起来那个内核日志里nfs报错的相关信息,我他么感觉,这个目录是不是网络存储啊,就是挂载到了nfs server。

然后我执行mount -l看了下,果然找到了nfs的踪迹:

这个挂载也就是说,/hxspace/production/files/unsafty/hxtg_dd挂载的是nfs服务器10.0.251.111的/home/app目录.

然后,我ping了下这个机器,不通。此时,基本确定就是这个问题了。

解决

所以,我猜测,df、lsof等各种要遍历文件夹的命令都卡死了,那估计nginx去读取那个目录下的文件,也卡死了,worker如果卡死,那么nginx负责accept的进程,应该就会停止去accept连接,进而导致全连接队列满。

但是,怎么验证是这个问题呢?

我们把nginx.conf中读取那个目录的配置先删了,然后再试,果然,这次全连接队列再没有积压了,肉眼看到的一直都是0.

但是,把别人配置删了也不合适,那看看能不能恢复nfs吧?

我们先去找服务器管理的同事,结果跟我们说,这个nfs服务器已经被回收了,果然,主打一个混乱。

行吧,反正是测试环境,既然nfs服务器没了,我们也没打算再搭一个,后边问到相关业务同事,已经没在用这台机器了,那就不用顾忌他们了,那这个挂载就得想办法去掉,不然各种命令都卡死,实在不爽。

去掉mount挂载,采用的umount命令,结果执行失败,提示device is busy。

后边查网上文章,需要ps aux找出状态为D的(也就是TASK_UNINTERRUPTIBLE,是一种不可中断的状态),我发现查出来的基本就是我之前卡死的那些df等命令。

kill 9强杀这些进程后,再去执行取消挂载,成功了:

[root@168assi ~]# umount -f -v 10.0.251.111:/home/app
/hxspace/production/files/unsafty/hxtg_dd: nfs4 mount point detected
/hxspace/production/files/unsafty/hxtg_dd: umounted

扩展:nfs

我们这里作为nfs客户端,挂载时,其实是可以指定选项的,其中有个选项如下:

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/deployment_guide/s1-nfs-client-config-options

也就是可以指定hard或soft,hard就是nfs服务端如果像我们这种情况,直接ip都不通了,客户端会一直死等,直到nfs server上线;而soft就是会超时,然后报错。

我们这边,这次就是挂载用了hard模式,不知道怎么考虑的。

10.0.251.111:/home//app on /hxspace/production/files/unsafty/hxtg_dd type nfs4 (rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=10.0.251.192,local_lock=none,addr=10.0.251.111)

总结

这次遇到的这个问题,说白了,最终就是因为关闭了nfs服务器的问题,也就能解释,为啥以前没问题了。

另外,我在写本篇的时候,感觉几年前好像研究过全连接队列的问题,去找了下之前文章,发现现象竟然是一模一样,几年下来忘得好干净,果然是唯手熟尔:

https://www.cnblogs.com/grey-wolf/p/10999342.html

问题排查:nginx能跑,但是只能跑一会,不能跑多了-LMLPHP

参考文章

https://www.cnblogs.com/yuboyue/archive/2011/07/18/2109867.html

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/deployment_guide/s1-nfs-client-config-options

https://superuser.com/questions/1562153/shutdown-of-rhel-6-9-7-7-nfs-client-hangs-when-nfs-server-has-become-unavailab

https://unix.stackexchange.com/questions/328746/how-can-i-monitor-the-length-of-the-accept-queue (查看accept队列内容)

https://krisnova.net/posts/linux-accept-queues/

08-31 15:20