为什么会报错
如果在Linux上运行Tomcat,则可能会遇到日志文件将呈指数增长情况,然后磁盘瞬间爆满,导致应用或容器重启。这不是tomcat的限制,而是操作系统OS的限制。
24-Nov-2021 15:56:27.590 SEVERE [http-nio-8080-Acceptor-0] org.apache.tomcat.util.net.NioEndpoint$Acceptor.run 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$Acceptor.run(NioEndpoint.java:456) at java.lang.Thread.run(Thread.java:748) |
代码层面作检查
我们的too many open file是读取文件没关导致的。
可以在tomcat报该错的时候,往前几分钟内遍历业务日志中的可疑日志,基本能找到原因,究竟在对文件作什么操作。
我们报错的时候在作这一句,跟代码进去可以发现
- 他是启动线程异步做reader.close操作的
- 如果要reader读取完毕后立即关闭文件,则需要使用try...with...resource语法
改造前
Files.lines(file.toPath()).forEach(text -> {
// todo 业务代码
}
改造后
try (Stream<String> lines = Files.lines(file.toPath())) {
lines.forEach(text -> {
// todo 业务代码
}
}
另外的参考案例java 8 - Do terminal operations close the stream? - Stack Overflow
改造前
Files.list(dirPath).forEach(filePath -> Files.lines(filePath).forEach(line -> { ... });
改造后
try(Stream<Path> dirStream = Files.list(dirPath)) {
dirStream.flatMap(filePath -> {
try { return Files.lines(filePath);}
catch(IOException ex) { throw new UncheckedIOException(ex); }
}).forEach(line -> { });
}
从运维层面解决
如果检查代码一切正常,就是业务激增导致的日志激增,那么只能从运维层面解决。
- 增大磁盘
- 增大文件数限制
可以通过如下命令查看OS的文件数量限制,然后调大限制即可(治标不治本,具体操作参照Tomcat Error: Too many open files)
ulimit -a
chuck@nowhere~$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 20
file size (blocks, -f) unlimited
pending signals (-i) 16382
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) unlimited
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited