您的位置:首页 > 编程语言 > Java开发

JAVA程序一次句柄泄露问题分析

2021-02-10 22:11 956 查看

在我们的性能测试中或多或少的都要参与些开发的工作,例如最常见的就是挡板的开发,因为在压测中往往不是单一系统会有一些关联系统,而这些关联系统不是我们测试的重点,为了最大限度的测试被测系统,关联系统就需要做成挡板模拟;而本次的问题就发生在挡板开发程序中,由于要最大限度得到被测系统性能,因此挡板性能要得到保证,一般我会将本地开发的java代码打包然后在Linux服务器上运行。本次压测中发现挡板程序会执行一段时间后发生异常,具体的分析过程如下。


挡板解释:

如下图我们一般测试系统重点为电商核心系统,但是电商核心系统又与第三方服务(支付、实名、短信等)交互,因此我们需要将第三方服务做成挡板进行模拟如图二。




句柄泄露问题分析:

1、当在执行正常的压测场景中,执行一段时间后交易全部失败,后来查找发现连接挡板超时,因此查看挡板的日志发现有大量的too many  open files的报错,当看到这个错误一定想到的是打开文件句柄数超了,首先要看看Linux的这方面的参数配置是不是小了改改看看。

在linux系统中可以通过ulimit–n查看每个进程限制的最大句柄数,通过ulimit –HSn 10240修改进程的最大句柄数。当句柄数目达到限制后,就会出现”too many  open files”。

通过ulimit -a查看对应参数:


原来设置为1024,通过ulimit –HSn 10240命令修改为102400进行验证。

2、修改完Linux参数后进行场景执行验证,发现执行一段时间后仍然出现问题,错误一样。这样我们会想到修改它其实治标不治本,本质的问题不在于参数而是代码本身,应该是代码本身出现了句柄溢出的问题。

1)首先我们知道如何查看Linux打开句柄数,验证是否不端正增长。

查看进程占用的句柄数有几种办法:

a、 通过cat/proc/pid/fd可以查看线程pid号打开的线程,cat   /proc/pid/fd |wc -l;

b、 通过lsof命令,需要root账号权限

查看当前系统的打开文件数:

# lsof | wc -l

# watch "lsof | wc -l"

可以用lsof -p <pid of process>看打开的文件句柄数.查看某个进程的打开文件数

#lsof -p  PID|wc -l

2)我们就使用lsof看看打开句柄数的情况,首先我们需要知道java程序运行的进程号,然后使用lsof -p  PID|wc -l查看

a、查看java进程号可以使用ps -ef|grep java找到我们的java进程ID,也可以使用jps命令

b、找到PID后通过lsof -p  PID|wc -l查看句柄数的使用

c、多次执行统计命令,发现数量一直在增加,并且越来越高直到超过系统参数设置102400后挡板程序异常。

3、一般出现句柄溢出的原因总结如下几点:

a、大多数情况是程序没有正常关闭一些资源引起的,所以出现这种情况,请检查io读写,socket通讯等是否正常关闭。

b、操作系统的中打开文件的最大句柄数受限所致,常常发生在很多个并发用户访问服务器的时候。因为为了执行每个用户的应用服务器都要加载很多文件(new一个socket就需要一个文件句柄),这就会导致打开文件的句柄的缺乏。

主要方向:在源码项目中查找所有引用了InputStream或者其他流的地方,查看其是否在使用完后,正常close掉,此处建议将流的close放到finally块中,这样异常时也会去close流。

次要方向:session或者其他有用到socket的代码处,仔细查询看是否有没有释放资源的地方

c、 如果检查程序没有问题,那就有可能是linux默认的open files值太小,不能满足当前程序默认值的要求,比如数据库连接池的个数,tomcat请求连接的个数等。。。

限制:有时是linux系统的参数的限制,需要修改内核参数。有时是中间件(weblogic、nginx)中启动文件参数限制,尤其是默认1024,在修改linux后若中间件有涉及也要同步修改。

4、找到代码问题并解决验证,当出现统计句柄持续增加的情况下,要lsof pid具体看看增加的是什么东西,发现都是打开的是读取的文件名,因为挡板代码中有读取文件的部分,仔细阅读代码发现每次读文件的流都没有关闭,是一个非常low的问题导致的。



5、将代码增加close,重新打包上传验证,执行12小时无异常问题,通过lsof pid|wc -l没有出现持续增加现象,问题解决。

小帮手:

1、一般我们简单些的程序,都会打成jar包然后上传到Linux上使用java -jar的方式启动,但是当我们的程序中存在于多个启动类的情况下,启动方法java -Xmx1024m -Xms2048m -cp com.example.socketdemo.server.HttpGet ,其中HttpGet为你制定的启动类。

2、当我们在Linux启动shell或者java程序中一般直接启动,最多加个&后台执行,但是当我们将当前session退出后我们启动的shell或者java程序会直接退出,正确的后台启动方法为:nohup start.sh>nohup.log 2>&1.


备注:

1、即使很小的代码问题也有可能导致很大的灾难,所以代码编写一定要走心。

2、本章重点是希望大家学会分析问题方法,而不是只局限于此问题,透过现象看本质。

原作者:liuhf
原文链接:JAVA程序一次句柄泄露问题分析
原出处:公众号
侵删


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: