另类原因导致的Too many open files错误

最近做开发,本地写好的程序没有问题,提交到局域网内的测试平台上,就经常报Too many open files错误,然后也经常报No reachable node in cluster错误(连接不到redisCluser)。

上网去搜,发现无一例外地,都说Too many open files错误是由于linux对文件句柄的限制数过小造成的,我们的环境也的确这个值较小,是默认的1024,让运维同学提高了还是一样。

No reachable node in cluster错误也是一样,redis的端口号能够telnet通,使用命令行直接在redis集群的服务器上操作也很正常,就是项目访问时,经常出现这个错误。

烦躁,在网上不停地换各种关键字搜索,最后找到了这篇文件《Java IO Stream句柄泄露分析》,里面提到:

当文件流未被显式关闭时,会产生怎样的后果?结果就是会引起文件描述符(fd)耗尽。以下代码会打开一个文件10次,向系统申请了10个fd。
……
但是即便如此,依然存在资源泄漏导致程序无法正常工作的情况,因为JVM规范并未对GC何时被唤起作要求,而对象的finalize()只有在其被回收时才触发一次,因此完全存在以下情况:在两次GC周期之间,文件描述符被耗尽!这个问题曾经在生产环境中出现过的,起因是某同事在原本只需加载一次的文件读取操作写成了每次用户请求加载一次,在一定的并发数下就导致too many open file的异常:

立刻以关键字input、output遍历整个项目,果然发现很多没有关闭流的文件,尤其是读取配置文件的那一个,也没有关闭流。这样原因就很明显了,因为没有关闭读取文件的流,加之文件句柄数较小,文件描述符很快用尽,再次访问时如果需要读取配置文件,就因为没有文件描述符,无法读取。配置都无法读取到,当然不可能建立对redis的连接。遇上个别添加了关闭流语句的程序,流被关闭,文件描述符被释放出来一个两个,于是在这之后的访问就可以正确读取到配置文件,连接到redis,从而程序可以正确访问。但是因为个数有限,文件描述符又很快被用尽,于是后面的人又无法正确访问了。

本地测试因为几乎没有并发,文件描述符被消耗的速度很慢,因此没有出现这个问题。

横展开修复了所有未关闭流的文件,果然,Too many open files错误再没有出现过