Too many open files (CLOSE_WAIT过多)的解决方案:修改打开文件数的上限值、调整TCP/IP的参数 (***有点靠谱)

发布时间 2023-08-30 14:03:27作者: kelelipeng

靠谱一点的参考: 

 https://blog.51cto.com/iosre/5686484

 

解决思路:修改打开文件数的上限值、调整TCP/IP的参数、代码层面及时主动关闭

​​另外还需要检查程序操作io的流是否在操作完之后关闭,这才是从最更本上的解决。​​

I 问题分析
1.1 分析方法
​​lsof​​ - list open files
lsof 是列出系统所占用的资源,但是这些资源不一定会占用打开文件号;比如:共享内存,信号量,消息队列,内存映射并不占用打开文件号;因此需要用命令​​ulimit -a​​查看open files 的最大数(当前用户的子进程打开的文件数限制,即imits.conf 文件配置信息)。

用命令​​ulimit -a​​查看open files 的最大数
# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
max locked memory (kbytes, -l) unlimited
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 7168

通过以上命令,我们可以看到open files 的最大数为1024。

在 Linux 上可用netstat查看服务器的 TCP 状态(连接状态数量统计):
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'


1.2 close_wait产生太多原因分析
close_wait 状态出现的原因:客户端要与服务端断开连接,先发一个FIN表示自己要主动断开连接了,服务端会先回一个ACK,这时表示客户端没数据要发了,​​但有可能服务端数据还没发完,所以要经历一个close_wait,等待服务端数据发送完,再回一个FIN和ACK。​​

close_wait产生太多原因:被动关闭方没有迁移到Last_ACK状态,也就是被动关闭方没有发送FIN包。

FIN包的底层实现是调用socket的​​close​​方法,被动关闭方没有执行close方法,说明服务端socket忙于读写,没有主动关闭 socket ,发 FIN 给 Client,此时服务端 Socket 会处于 CLOSE_WAIT 状态,而不是 LAST_ACK 状态,致使监听 port 打开的句柄数到了 1024 个,且均处于 close_wait 的状态,最终造成配置的port被占满出现 “Too many open files”,无法再进行通信。

 

第一次挥手(FIN=1,seq=x)

假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包(终止包),表示自己已经没有数据可以发送了,但是仍然可以接受数据。

发送完毕后,客户端进入 FIN_WAIT_1 状态。

第二次挥手(ACK=1,ACKnum=x+1)

服务器端确认客户端的 FIN(终止) 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。

发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。

第三次挥手(FIN=1,seq=y)

服务器端准备好关闭连接时,向客户端发送结束连接请求,发送一个FIN终止包,FIN 置为1。

发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。

第四次挥手(ACK=1,ACKnum=y+1)

客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。

服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。

 

II CLOSE_WAIT过多的解决方法
2.1 代码层面
代码层面及时主动关闭:

使用完socket就调用close方法;
socket读控制,当读取的长度为0时(读到结尾),立即close;
如果read返回-1,出现错误,检查error返回码。如果不是AGAIN,立即close。
INTR(被中断,可以继续读取)

WOULDBLOCK(表示当前socket_fd文件描述符是非阻塞的,但是现在被阻塞了)

AGAIN(表示现在没有数据稍后重新读取)。

如果调用 ServerSocket 类的​​accept()​​​方法和 Socket 输入流的​​read()​​​方法时引起线程阻塞,应该用​​setSoTimeout() ​​方法设置超时(超时的判断是累计式的)。
缺省的设置是0,即超时永远不会发生。

 

2.2 调整TCP/IP的参数
tcp_keepalive_time: 允许的持续空闲时长(每次正常发送心跳的周期)
tcp_keepalive_probes :在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认值为9(次)
tcp_keepalive_intvl:在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包的发送频率,默认值为75s。
sysctl -w net.ipv4.tcp_keepalive_time=600
sysctl -w net.ipv4.tcp_keepalive_probes=2
sysctl -w net.ipv4.tcp_keepalive_intvl=2

#sysctl 配置与显示在/proc/sys目录中的内核参数: 可用sysctl来设置或重新设置联网功能,如IP转发、IP碎片去除以及源路由检查等。用户只需要编辑/etc/sysctl.conf文件,即可手工或自动执行由sysctl控制的功能。
# -w 临时改变某个指定参数的值,如

如果临时改变参数后,可起作用,则修改​​/etc/sysctl.conf​​使其永久生效。

net.ipv4.tcp_keepalive_time = 1800 #默认值是7200(2小时)
net.ipv4.tcp_keepalive_probes = 3 #默认值是9,TCP 发送 keepalive 探测以确定该连接已经断开的次数
net.ipv4.tcp_keepalive_intvl = 15 #默认值为75,探测消息发送的频率

编辑完 /etc/sysctl.conf ,要重启 network 才会生效:​​/etc/rc.d/init.d/network restart​​

2.3 调整系统句柄相关参数 :详见本文第三章节
III 修改打开文件数的上限值
​​/proc/sys/fs/file-max ​​ 是整个系统可以打开的文件数的限制,由 sysctl.conf 控制;

如果 cat /proc/sys/fs/file-max 值为 65536 或甚至更大,不需要修改该值;

3.1 临时改变open files 的值
ulimit 修改当前 shell 和它的子进程可以打开的文件数的限制,由 limits.conf 控制;

ulimit
1.
3.2 永久设置open files 的值
1.使用root登陆, /etc/security/limits.conf 添加如下一行:

登录后复制
#<domain> <type> <item> <value>

* soft nofile 8192
* hard nofile 8192
#* - nofile 8192
#xxx - nofile 8192
#xxx 是一个用户,如果是想所有用户生效的话换成 *
#设置的数值与硬件配置有关,别设置太大了。
 
生效: 确保​​/etc/pam.d/login ​​​和​​/etc/pam.d/sshd ​​​包含​​session required /lib/security/pam_limits.so ​​,然后用户重新登陆一下即可生效。
session required /lib/security/pam_limits.so
-----------------------------------


©著作权归作者所有:来自51CTO博客作者#公众号:iOS逆向的原创作品,请联系作者获取转载授权,否则将追究法律责任
Too many open files (CLOSE_WAIT过多)的解决方案:修改打开文件数的上限值、调整TCP/IP的参数
https://blog.51cto.com/iosre/5686484