一、故障说明
今天登录系统总是显示失败,查看日志的时候,发现日志中报出了大量的异常Too many open files
。根据经验判断应该是操作了大量的文件并且在操作后,没有关闭文件流。
2022-03-29 22:22:47 [http-nio-8777-Acceptor] ERROR org.apache.tomcat.util.net.Acceptor : Socket accept failed
java.io.IOException: Too many open files
at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:461)
at org.apache.tomcat.util.net.NioEndpoint.serverSocketAccept(NioEndpoint.java:73)
at org.apache.tomcat.util.net.Acceptor.run(Acceptor.java:95)
at java.lang.Thread.run(Thread.java:748)
二、too many open files描述
too many open files是Linux系统中常见的错误,从字面意思上看就是说程序打开的文件数过多,不过这里的files不单是文件的意思,也包括打开的通讯链接(比如socket),正在监听的端口等等,所以有时候也可以叫做句柄(handle),这个错误通常也可以叫做句柄数超出系统限制。
三、故障排除
引起的原因就是进程在某个时刻打开了超过系统限制的文件数量以及通讯链接数,通过命令ulimit -a
可以查看当前系统设置的最大句柄数是多少:
[root@test]# ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 1029324
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 65536
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) unlimited
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
open files那一行就代表系统目前允许单个进程打开的最大句柄数,这里是65536
因为我是用的是docker,里面仅仅启动了一个java程序。为此当我们使用top命令查看的时候,此处主要是为了获取程序的pid。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 7403544 807388 13820 S 1.7 0.3 100:29.02 java
21267 root 20 0 15388 2112 1636 S 0.0 0.0 0:00.01 bash
21283 root 20 0 59628 2124 1504 R 0.0 0.0 0:00.01 top
第一次操作lsof命令可能需要安装:
yum install -y lsof
接下来看我这个java程序到底打开了多少文件。此处使用命令:lsof
。
lsof -p 1 |wc -l
# 返回值:65572
此时发现,进程1,也就是我们的java程序操作了65572个文件,并且没有关闭。超过了我们系统允许的数量65536。
如果是普通的虚拟机或者物理,可以通过如下命令,找到打开文件数量最多的进程:
#查看了排在前面的几个进程
lsof -n | awk '{print $2}' | sort | uniq -c | sort -nr | more
重新回到我们刚才的java 程序操作中,我们发现java程序的打开的文件太多了,我们将其操作的文件导出到文件中,在文件中查看。
lsof -p 1 > openfile.log
在导出的文件中找到打开次数最多的文件,然后在代码中排除即可。
我们的代码中是重复操作了同一个文件,文件名相同,比较好找,如果文件名字不同,可能需要开发人员细心排除一下了。
四、其他解决方案
如果故障原因不是因为文件没有关闭,或者项目需要开启更多句柄,可以修改配置文件,从而允许打开更多文件。
1、临时修改方案,重启失效。
# 将句柄设置为102048
ulimit -n 102048
2、永久方案
sudo vim /etc/security/limits.conf
文件的最后添加
* soft nofile 65535
* hard nofile 65535
最终效果:
#<domain> <type> <item> <value>
#
#* soft core 0
#root hard core 100000
#* hard rss 10000
#@student hard nproc 20
#@faculty soft nproc 20
#@faculty hard nproc 50
#ftp hard nproc 0
#ftp - chroot /ftp
#@student - maxlogins 4
* soft nofile 65535
* hard nofile 65535
最前的 * 表示所有用户,可根据需要设置某一用户
然后修改全系统总限制
sudo vim /etc/sysctl.conf
在文件底部添加:
fs.file-max=655350
立即生效:
sudo sysctl -p
这样就修改完毕了,用户级句柄数的修改需要重启一下才能生效,最好执行一下reboot,再次输入ulimit -n查看已经修改好了。