线上故障分析 Socket accept failed java.io.IOException: Too many open files 导致的系统宕机问题

1、项目运行环境, CentOS Linux release 7.4.1708,springboot 2.1.0.RELEASE,jdk1.8,mysql 5.6.16

 

2、系统应用,在这里简称为应用A,在某个周末的时候,无法正常运行,连续抛几十分钟异常java.sql.SQLNonTransientConnectionException: Could not create connection to database server,导致系统宕机,截图如下:

3、最开始以为是连接不上数据库,当时telnet ip port,发现mysql数据库服务端网络也是正常的;

是否数据库连接池的设置问题,然后去看了一下连接池配置,需要增大吗,是否越大就越好?可以参考这篇博客:

https://blog.csdn.net/educast/article/details/93461372(数据库连接池的大小值设定,不能随意设置太大的值)

看完以后,发现系统设置的初始化大小:5,最大:20,好像并没有什么问题

 

4、是不是系统在某个时间点,由于网络原因,连接不上服务了,解析域名的时候报错,当网络恢复以后无法正常连接?发现以下连接设置,有autoReconnect=true参数,当网络恢复的时候,会自动进行数据库重连接,也不至于导致系统宕机。

spring.datasource.url=jdbc:mysql://*************.mysql.rds.aliyuncs.com:3306/**********?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&autoReconnect=true

下面验证一下是否会进行自动重连,在测试环境配置一个数据库访问域名,先断开网络,报异常如下:

 

恢复网络,发现预编译sql都打印出来,并且执行,说明autoReconnect=true参数是有作用的,并且不会由于网络临时抖动问题,导致数据一直抛异常:java.sql.SQLNonTransientConnectionException: Could not create connection to database server

 

 

5、在发生宕机的同一台服务器上,我们还有另外一台应用B;A应用和B应用部署在同一台服务器上,用的同一数据库连接配置,这种宕机的问题,发生过两次,都在A应用,同时间B应用却没有找到这样的异常日志,我们再把这个异常日志分析一下:

在SQLNonTransientConnectionException异常发生之前,有很多 Socket accept failed
java.io.IOException: Too many open files

那么数据库连接异常 和 Too many open files有什么关系呢?

在linux环境下,在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件:普通文件,目录,NFS文件,字符文件,管道,Socket流,网络Socket等等,并且可以通过lsof命令,查看系统打开文件大小,或者某个pid占用文件大小;使用ulimit命令,查看默认分配占用文件大小;

lsof -p  pid | wc -l,查看pid进程打开了多少个文件句柄:

关于上面所说的可以参考如下博客链接:

参考连接,lsof(list open files)命令:https://blog.csdn.net/qq_27870421/article/details/92803453

limit资源限制,ulimit命令 详解:https://blog.csdn.net/skiwnc/article/details/84100095

 

6、并且在lsof命令,查看到很多/data1/hrbb/file/temp的占用,一共700多

应用程序启动路径是在/data1/hrbb/下面,所以程序中有很多占用路径/file/temp的资源?,那么去代码中找一下是否有file/temp资源使用的地方呢?

找到一处localPath变量:

 

7、发现文件资源,使用完,没有关闭,导致大量/data1/hrbb/file/temp 占用文件句柄;

并且在finally里面的delete()方法,由于没有关闭流,也是删除不掉文件的(这一点已经在本地写测试代码,验证过了,就不贴出来了);

然后我们去看一下这里代码的提交记录对比,左边代码是之前原始版本,右边是误修改后的版本

try(){}catch{},和 try{}catch{}是不一样的(注意第一个try语法后面的小括号)

原来是用了 try-with-recourse 语法;

 

8、所以问题大概就出在文件使用资源上面,再从日志里面搜一下,Too many open files第一次出现的地方

grep -C 200 'Too many open files'    ***.log | more
file/config/EcspPublicKey.cer证书文件,无法读取;那么我们再去看代码,是否还有其它使用文件资源的地方,没有释放呢?

 

9、在IDEA中继续全局搜索  FileInputStream使用地方:

找到demo代码如下,168~174行被修改过,这里的InputStream和OutPutStream都没有关闭,

 

10、Java库中有很多资源需要手动关闭,比如InputStream、OutputStream、java.sql.Connection等等。在此之前,通常是使用try-finally的方式关闭资源;Java7之后,推出了try-with-resources声明来替代之前的方式。 try-with-resources 声明要求其中定义的变量实现 AutoCloseable 接口,这样系统可以自动调用它们的close方法,从而替代了finally中关闭资源的功能;

我在本地写了一段测试代码,在try()中声明IO流对象,并没有手动关闭资源,打包后反编译class文件,可以看到编译器识别到了语法糖,自动加上了资源释放代码:

想必修改的人,可能不知道这个语法,而且也忽略了关闭资源,导致了资源泄漏的问题;

关于 try-with-recourse 详细语法细节问题,可以参考帖:

https://blog.csdn.net/weixin_40255793/article/details/80812961?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase

11、然后回到代码继续找,没有关闭资源的地方:

这里,关闭了ObjectInputStream,却没有关闭FileInputStream :

还有这里,如果抛异常,也会导致资源无法关闭:

12、回到应用服务器上,ulimit -a,查看单个pid允许最大文件数:1024

之前抛异常的应用程序,pid为26562 ,目前已经达到557,昨天看还是400多;

希望下次出故障的时候,能再分析,确认一下是这个文件的问题;

修复方式:更改代码,finally中,释放资源,或者用try with resource方式。

上线待验证,后续是否还会出现此类问题

 

13、在另一天程序中出现的同样问题:

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值