事故现场
服务在运转一段时间后,体系监控中load目标飙升到峰值,频频呈现请求失利告警,最终服务因无法建立新的socket衔接,导致服务雪崩。必须经过重启服务的办法才干康复正常。
问题定位
刚开始以为是oom,可是没有发现oom日志,而且仓库运用和gc情况都在正常的范围内。然后在服务日志发现很多的错误信息:Too many open fils(或 翻开的文件过多)。
Too many open files:句柄数超过体系约束,也是Linux体系中常见问题,这里的 files 不仅是体系文件,还包含请求衔接 socket,端口监听等。
发生原因
经过排查后确定是因为在加载暂时数据文件后删除文件没有封闭stream释放句柄,所以服务在运转一段时间后呈现 Too many open files。
句柄原理
在Linux体系中,目录、字符设备、块设备、套接字、打印机等都被抽象成了文件,即一般所说的“全部皆文件”。程序操作这些文件时,体系就需求记载每个当前拜访file的name、location、access authority等相关信息,这样一个实体被称为file entry。这些实体被记载在open files table中,Linux体系配置了open files table中能容纳多少file entry。假如超过这个配置值,则Linux就会回绝其他文件操作的请求,并抛出Too many open files。
处理进程
(1)检查体系句柄约束数
经过 ulimit -a 命令,能够看到 open files 便是允许单个进程翻开的最大句柄数,默许是1024 。
[root@kvm-10-12-10-55 ~]# 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) 31211
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) 31211
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
(2)找到服务进程PID,检查该进程已翻开的文件句柄。
[root@kvm-10-12-10-55 ~]# ps -ef | grep syx-service
root 30118 25539 0 15:51 pts/3 00:00:21 java -jar syx-service-0.0.1-SNAPSHOT.jar
[root@kvm-10-12-10-55 ~]# lsof -p 30118 | wc -l
1024
[root@kvm-10-12-10-55 ~]# lsof -p 30118
java 30118 root *925r REG 253,0 26 408780 /tmp/a01.txt (deleted)
java 30118 root *926r REG 253,0 26 408780 /tmp/a02.txt (deleted)
java 30118 root *927r REG 253,0 26 408780 /tmp/a03.txt (deleted)
java 30118 root *928r REG 253,0 26 408780 /tmp/a04.txt (deleted)
java 30118 root *929r REG 253,0 26 408780 /tmp/a05.txt (deleted)
java 30118 root *930r REG 253,0 26 408780 /tmp/a06.txt (deleted)
java 30118 root *931r REG 253,0 26 408780 /tmp/a07.txt (deleted)
java 30118 root *932r REG 253,0 26 408780 /tmp/a08.txt (deleted)
java 30118 root *933r REG 253,0 26 408780 /tmp/a09.txt (deleted)
java 30118 root *934r REG 253,0 26 408780 /tmp/a10.txt (deleted)
...
(3)正常情况下,能够经过增大 open files 的办法来处理这个问题,可是我所呈现问题采用这个办法,是不能彻底处理的,只能定位到问题代码来处理。
- 暂时修正文件句柄数约束参数
ulimit -n 2048 # 重启后会康复默许值,非root用户只能设置到4096
- 经过修正配置文件永久修正
[root@icoolkj ~]# vi /etc/security/limits.conf
# 文件末参加
* soft nofile 655360
* hard nofile 655360
(4)定位问题代码。
经排查服务日志发现,定位到代码是 long count = Files.lines(Paths.get(path)).count(); 没有封闭stream导致。
Files.lines()的运用办法在官方文档中是这么说的:
If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream’s close method is invoked after the stream operations are completed.
原因
- Files.lines() 这个办法,没有封闭翻开的文件
- 假如需求周期性的读取文件,需求运用try-with-resources语句来确保stream的close办法被调用,然后封闭翻开的文件。
处理
修正为下面写法,即可处理啦。
try(Stream<String> stream = Files.lines(Paths.get(path))){
count = stream.count();
} catch (IOException e){
e.printStackTrace();
}
等价于
Stream<String> stream = null;
try{
stream = Files.lines(Paths.get(path));
count = stream.count();
}catch (IOException e){
e.printStackTrace();
}finally {
if(stream != null){
stream.close();
}
}
以上,希望对遇到同样问题的小伙伴有协助。
相关链接:<Files.lines()办法运用相关问题> blog.51cto.com/u_15185289/…
最终,祝我们圣诞快乐,Merry Christmas !!!