【闲说】性能测试
版权声明:本文为本作者原创文章,转载请注明出处。感谢 码梦为生| 刘锟洋 的投稿
性能测试是一件看起来不简单,操作起来确更困难的事情,我认为,每认真做一次性能测试,一定会有不同收获,而每次性能测试暴露的问题,现象都不是仅仅涉及Java,tomcat这么简单,简单说就是光会写代码是无法做好性能测试的。
那么,就趁着这次性能测试的机会,重新梳理下对linux,网络IO等基本功的认识已经就性能瓶颈的定位分享下自己的心得。
背景:
本次性能测试的目的是测试使用公司内部RPC框架开发的一套接口的性能,目的是准确的拿到接口的性能指标,当然,我也想换个角度去看待代码,比如,从性能或者对操作系统友好的角度,首先略过1万字的环境搭建过程,其实,性能测试是件非常考验细心程度的活,需要你对整个组网环境,调用关系,业务逻辑非常清晰,能正确识别出压力机,应用服务器,数据库服务器,缓存服务器各自的职责,并在可能的情况下压榨其性能极限,以及清楚明白可能对其性能造成影响的命令和操作(比如jvm启动参数中,大部分日志打印相关参数,关闭即时编译,调整GC分带的大小和比例等等)。
其次,还需要你能清楚本次测试程序的核心流程,业务逻辑,尽可能的将一些非核心的组件可能造成的影响去除掉,比如,本次测试的是RPC接口的性能和业务处理的效率,而相应涉及到的分布式缓存,数据库,则不是本次测试的重点,因此,组网时,应该尽可能将分布式缓存,数据库等机器与应用服务器放在同一个局域网网段内甚至同一台机器上,保证不会因为他们的表现而直接影响测试目标测试结果。总之,Mock掉一切非核心,不相关的因素。
测试工具/命令
因为是对RPC接口做测试(而非HTTP接口),使用loadRunner等测试工具无法满足要求,所以在测试工具的选择上,使用了jmeter。这是一款100%java实现的性能测试工具,使用方式是,继承他的AbstractJavaSamplerClient抽象类,在其runTest方法中编写测试用例即可在Jmeter的客户端中发现测试jar包,从而配置线程数后开始性能测试。具体使用请google之。
在压力机上部署好jmeter后,多线程开始执行测试用例,用例开始向应用服务器发送调用请求。这个时候设置的并发数应该尽量让压力机的CPU达到一个较高的值,比如 70%左右,jemeter提供了预热功能,必要时候可以使用预热功能将压力慢慢加大。
下面,我们借这次性能测试的机会介绍几个很好用的命令,启动后可以先到应用服务器上使用top命令,观察其CPU利用率,按数字键1,详细查看每个CPU的利用率。 理想情况,这个时候应该可以看到一定的压力。
也可以使用 vmstat命令,比如: vmstat 1 30 标示,每隔一秒打印一次统计信息,统计30秒。
通常主要关注r 和b 分别代表运行队列的数量和阻塞个数。理想情况应该是r比较多,b没有。
具体vmstat命令的结束可以看:http://www.cnblogs.com/ggjucheng/archive/2012/01/05/2312625.html
关注完CPU,还可以使用iostat命令 查看IO的情况,命令格式类似vmstat, iostat 1 30 表示同样的意思:每秒打印一次统计信息,打印30次后退出。
这里使用iostat -dx 1 每隔一秒打印一次磁盘的详细信息 具体可以参看这里:http://www.cnblogs.com/peida/archive/2012/12/28/2837345.html
可以看到io并不频繁。都是在很低 的水平上,因此IO应该不是此次性能测试的瓶颈所在。
看完vmstat,iostat,如果都不是处在很高的水平,下一个应该优先看网络,是的,对于linux还有一个netstat,使用方式与前两种不同,但是功能同样强大,可以列出应用服务器上所有tcp连接的详细信息。通常情况,可以观察下应用服务器上的TCP连接数,连接状态是否正常等来判断应用是否运行正常。
很好用的工具,动手输一下,你就会知道它的功能了。
测试目的
一般测试的目的有两个:
1. 是获取接口的性能,常用TPS,延迟,QPS等衡量。
2. 是通过性能测试,找出性能瓶颈,定位并优化它。
因此,性能测试必须要满足的一个原则是,要么把压力机跑满,要么让应用服务器的某项指标跑满,之所以说某项指标,是因为,根据应用的不同,例如:CPU密集型应用,IO密集型应用,相应的,性嫩测试的时候应该尽量让CPU利用率或者IO利用率达到一个较高的水平。
如果有一项指标没有达标,那性能瓶颈的定位过程就来了。
瓶颈定位
要定位瓶颈,首先需要知道应用服务器的哪项指标没有达到预期,是CPU利用率还是IO利用率或是都不高,这可以通过以上介绍的三个stat工具观察出来,如果没有看到哪项值比较高,至少也可以得到“都不高”的结论,而都不高的时候,或许意味着你的应用程序不像你想象的那么高效,一定,一定,在某个点上正在激烈的竞争者或者阻塞。程序会平白无故效率低下!
当然除了通过利用率,可以看到CPU或者IO的忙闲程度,有时我们还需要知道具体的数字,比如,哪种IO才算繁忙?
举个例子,这次性能测试时我使用了一个工具:ifstat,它并非系统自带的工具,需要手动安装,安装过程非常简单,自行google即可。
安装完毕后,将ifsta命令加入到path变量中,即可在全局使用ifstat命令,使用后的效果大致是这样:
eth0 代表了操作系统的第一张网卡。从这里可以看到此时这张网卡的读入速率是15M,出口是8M左右,而这张网卡的速率为:
也就是1000Mbit/s ,因为8B10B编码的关系,换算后理论的带宽为100M/S,100:15 利用率为15%,这个时候我们才可以说IO的利用率不高。IO不算繁忙。
同样的,有时会出现一种情况:CPU,网络/磁盘IO都不高,同样TPS也不高,这个时候可以从程序入手,分析下是否是程序执行缓慢的原因,原因大致有这么几种:
1. 有代码在串行的某件事,比如打日志(这次我就遇到了因为我使用自己写的工具监控方法执行时间的关系,在性能测试时有大量打印日志和计算工作 ,导致应用程序TPS非常低,拿掉工具后TPS才恢复到一个正常的水平,当然,这就不是说我的工具就不好用(笑) ,有兴趣的可以看下工具的介绍,总的来说还是很好用的,工具地址: https://github.com/liuinsect/Profiler)。
2. 多线程竞争,比如,多线程环境下,锁的竞争,对象监视器的竞争,数据库,分布式缓存连接的竞争(如果使用连接池的话)等。
3. 过于冗长,复杂方法的执行,虽然JIT可以执行一定程度上的优化,但是,糟糕代码无下限,要想写得烂,总是可以的。
4. 所依赖的系统慢,比如,数据库,分布式缓存慢(当然,他们的慢有更多种可能,有时候甚至需要找到他们慢的原因,并mock掉)。
这个时候就要借助visual vm等工具了。
因为很多时候我们是想在本地,去连接远程服务器上的JVM,这就涉及到了远程连接JVM的问题,可以这么做:
1.首先必须在远程机器上面启动jstatd这个后台进程。它位于JDK安装路径的bin目录里面。配置java安全访问,在jstatd所在的目录的下新建文件jstatd.all.policy,在我的机器上是/usr/java/jdk1.7.0_05/bin
1 |
grant codebase “file:${java.home}/../lib/tools.jar” { |
2 |
3 |
permission java.security.AllPermission; |
4 |
5 |
}; |
注意结尾还有一个分号。
2.然后用如下命令启动jstatd:
jstatd -J-Djava.security.policy=jstatd.all.policy
正常启动没有任何输出。默认打开端口是1099,也可以通过-p 参数设置端口。
3. 在本地打开 visual vm ,输入远程服务器的IP后,即可连接了。
当然,因为只是启动了jstatd,通过visual是不能使用线程监控,CPU监控的,如果需要用到这些功能的话,还需要使用JMX去远程连接tomcat 。方法在这里:http://www.oschina.net/question/162973_105064
通过visual vm 已经可以观察到GC的情况,运行的线程数,是Block,Running 等等情况了,如果应用程序竞争激烈,应该会看到线程运行条上有一段一段的红色区域,代表线程正在被频繁阻塞,再想深入,可用通过visual vm 将线程情况dump下来(当然,也可以在应用服务器上使用jstack -m <pid>),查看线程都被哪些对象阻塞,线程的调用栈是什么样子的, 从而定位到,应用程序中哪段代码有频繁竞争,为什么竞争,是否可以优化等。
当然,除了使用visual vm 还可以使用linux上的另外一个工具 perf ,他是linux 自带的性能分析工具。可以通过它看到系统执行的性能情况,功能实在强大,比如:
1. 列出L1,L2,L3 Cache的命中率(需要硬件支持,这次我们性能测试的机器不支持它)
2. 列出系统/进程的性能统计信息 命令: perf top -p pid
3. 分析程序的整体性能 perf stat
等等, 具体可以参考这里: http://iamzhongyong.iteye.com/blog/1908118
关于perf,这次还有一个小小的故事,在测试接口的某个方法时,使用perf观察到JVM的反射相关的方法耗时开销非常高,这才想起来反射相关的代码没有对反射的结果做缓存,因此果断回去加上缓存后,看到前面的方法在perf的列表中消失,这才放下心。
总结:
通过以上介绍的性能测试的思路和工具,我们基本上可以完成一次性能测试以及部分问题的性能定位,但是往往性能问题总是隐藏得很深的,并且受各种条件的影响,比如,各个环节的配置参数,网络情况,机器情况,性能测试的工具等等,所以,性能测试的结果往往不能脱离某个环境单独比较,不同的配置,环境,应用的性能都会呈现出不同的结果,出现问题时,也需要我们从前到后,从上到下仔细分析每一个流程的执行情况,逐步通过工具协助定位,才能最终找到瓶颈。总之,性能测试是一门考验耐心,细心,知识广度, 深度的活,每次遇到问题多问几个为什么,多做几次分析和验证,并尝试解决,优化它,一定会让你对系统有更多不一样的认识。