在我们的性能测试中或多或少的都要参与些开发的工作,例如最常见的就是挡板的开发,因为在压测中往往不是单一系统会有一些关联系统,而这些关联系统不是我们测试的重点,为了最大限度的测试被测系统,关联系统就需要做成挡板模拟;而本次的问题就发生在挡板开发程序中,由于要最大限度得到被测系统性能,因此挡板性能要得到保证,一般我会将本地开发的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程序一次句柄泄露问题分析
原出处:公众号
侵删