问题背景
线上项目在执行 ftp 下载的时候报如下错误
java.net.SocketException: Too many open files
at java.net.Socket.createImpl(Socket.java:460)
at java.net.Socket.getImpl(Socket.java:520)
at java.net.Socket.setSoTimeout(Socket.java:1141)
at org.apache.commons.net.ftp.FTPClient._openDataConnection_(FTPClient.java:917)
看报错原因:是该服务(某个进程)打开了超过系统设置的文件句柄数量
问题定位
首先找到报错的这一台服务器
然后使用命令查看文件句柄配置
[~]# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 127906
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024 # 看这里
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) 127906
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
可以看到,该配置是默认的 1024 配置,比较小,我们先来确定到底是哪一个进程导致的
首先在本例中,明确的知道是哪一个进程(服务)报错的,就不用下面这一个步骤了
[~]# lsof | awk '{print $2}' | sort | uniq -c | sort -n -r | head -n 10
1404071 93486
42066 2014
37449 103586
25414 65935
20064 1898
11978 32574
6956 110448
435 1281
306 764
258 762
上述命令目前笔者不清楚第一列是什么意思,第二列是进程号,是按当前系统中打开文件句柄数量倒序排序的。
查看这个进程目前打开的句柄详细信息
lsof -p 93486
# 或则使用如下命令将信息导入到文件中
# lsof -p 93486 > openfiles.log
# 然后使用命令查看详细信息:N 显示行号,m 显示百分比
# less -N -m openfiles.log
java 93486 root 804r REG 8,17 171061964 34342129 /xxx/temp/ce48058c1924476fb392c20a60fa4bb2-20210311175647.txt (deleted)
java 93486 root 805r REG 8,17 6354790 34342150 /xxx/temp/b61f85f794ab4dd2847061daaf2d43fb-20210311175756.txt (deleted)
java 93486 root 806r REG 8,17 168412335 34342151 /xxx/temp/627ba21b01da435eabeaeda35f7f441c-20210311175929.txt (deleted)
java 93486 root 807r REG 8,17 12839463 34342167 /xxx/temp/77755adb5271453da1fb53caf945c2f8-20210311180159.txt (deleted)
java 93486 root 808r REG 8,17 168410493 34342168 /xxx/temp/3d8e65f13ea14a0e9c95bddab5b99bc9-20210311180312.txt (deleted)
java 93486 root 809r REG 8,17 478 34342221 /xxx/temp/e9b11e6ab6034cf8bffb218c38803bab-20210302220706.txt (deleted)
java 93486 root 810r REG 8,17 5639576 34342186 /xxx/temp/d8d2d7938a994e3a94236fecefd54135-20210311180836.txt (deleted)
java 93486 root 811r REG 8,17 168399280 34342194 /xxx/temp/cc7d5c81d41f4d9080197aedf8a44f70-20210311180949.txt (deleted)
通过分析(使用文件的方式打开,可以显示行号),可以看到这个进程打开了 4000+ 的句柄,如上所示,大部分都是打开了一个文件。
所以这里猜测是代码中,要么是读或则是写之后没有正确的释放这个资源导致的。
解决方案
通过如上定位,首先是打开了那么多的临时文件,根据文件名称判断,好一两个月前的都还没有被释放,并且这个文件都已经被删除过了,那么一定是代码有问题,那么现在的解决方案有两个:
- 先临时修改单个进程的打开文件句柄数量,重启服务后,先恢复服务
- 然后去排查代码
修改打开文件限制数量
临时修改最大文件打开数 ulimit -n 修改数
如
ulimit -n 65535
永久生效需要修改配置文件 /etc/security/limits.conf
* - nofile 65535
# 或则分别写
* soft nofile 60000
* hard nofile 65535
有可能需要重新启动,所以,先临时修改,再修改配置文件,等可以重启机器的时候再重启
代码排查
通过打开的文件内容,基本能确定是在某一块业务相关的代码,由于这一块的业务逻辑有点复杂,代码量也较多,找了很久都定位不了到底是哪里没有释放资源
lsof 的其他使用
查看哪个进程占用了文件
[~]# lsof /xxx/temp/44982c03646d4c97a5b47808dc3988a6-20210507180004.txt
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 50958 root 385r REG 8,17 118009 34341699 /xxx/temp/44982c03646d4c97a5b47808dc3988a6-20210507180004.txt