某天突然接到隔壁部门的任务,需要帮忙对他们的API进行性能测试,对方大佬的要求非常简单:需测试2个接口,目前常规脚本执行速度大约在0.02秒一条数据,而该API是提供给部门内部人士使用的,允许在开多线程的时候,单条1-2秒,且速度不下降。再询问具体的数据,以一句专业人士来处理把我给打发走了。
我继续分析该要求,看目的是想进行压测,再分析下他们部门人数,并不多,十来个的数量,找对方开发要来了需进行压测的服务器的信息以及接口的数据,发现请求体和响应体的数据量也很低,话不多说,开始性能调优之旅。
1.调通接口
性能测试的起步是接口得先测通,先用Jmeter调用两接口,都能跑通
2.准备测试数据
为保证性能测试的结果偏近于生产环境,也由于对方业务的特殊性,于是继续找他们大佬索要测试数据,对方直接给了一万条数据,直接把测试数据放到CSV文件里,运行1条数据检查效果。
2个接口1个为get请求,1个为post请求,其中post请求是json格式的。
由于该接口仅供内部人士使用,因此有key(类似token)值即可使用
2.1测试数据的参数化
get请求的API设置
post请求的API设置
执行后查看结果,没问题。
3.查看压测环境的硬件信息
由于信息量太少,因此很多事情只能靠自己,通过硬件信息+对方人数来推测至少要达到的并发量。
3.1经典top命令
通过top命令查看硬件资源的使用情况,再按1可以看到CPU是8核,且后续可以查看每个CPU的使用情况。
通过MiB Mem可以看到内存是16008MB,也可以使用free -m命令
3.2 free -m查看内存使用情况
3.3 df -h查看磁盘使用情况
排除回环设备外(use%=100%),其他的使用率也非常低。
3.4 dstat命令查看当前内存、I/0、CPU占用最高的进程信息
通过dstat --top-mem --top-io --top-cpu命令可以看到目前系统资源并未被过多占用。
3.5 dstat -n查看网络读写情况
由于公司内部使用的是内网环境,除非断网,基本可以忽略网络影响的情况。
4 查看初测结果
经过以上的一番操作,可以对自己要测试的环境有清晰的了解,同时也能得出结论:该环境完全支持对方部门的正常并发操作。于是我先把线程数设置到20来体验下,然而结果相当糟糕。
首先设置并发数:
添加聚合报告后查看运行结果:
从聚合报告可以清晰地了解到:两个接口的平均响应时间在1分钟+,最小响应时间分别是11.5S和41.1S,报错率分别为59.72%和80.46%,由于响应时间相当不理想,因此导致吞吐量也非常高。 但根据对服务器的运行表现,CPU和内存的使用率是非常低的,和压测前的表现相差极小,因此得从软件方面找原因。
1.查看接口的报错响应,发现都是两方面的报错:
一是POST请求的页面被压崩了,更为详细的信息通过查看服务器的日志来寻找蛛丝马迹
二是连接数据库超时导致报错,结合硬件的使用率异常低来看,很有可能有俩个原因导致这个错误:一是数据库的连接数过少,二是数据库的连接方式有问题。具体是哪种情况导致的呢?可以用过具体分析来解惑。
5.jmap命令分析问题
想使用jmap命令,得先知道相对应的进程ID
5.1获取java的进程
通过ps aux |grep java来获取进程ID
5.2查询数据库连接数
首先获取管理员权限,切换用户:sudo -i
再通过 jmap -histo 进程ID|grep com.mysql.jdbc.JDBC4Connection 得知连接数为4(看中间的数值,通过重新配置后已调整为22),跟开发沟通后,除了连接数量较低外,还需加索引。优化后进行第二轮测试。
同样的压测设置来看执行结果,相当漂亮的结果,响应时间和报错率都得到了质的提升,达到了隔壁大佬的要求,再看下服务器使用率还是和之前一样,没啥大变化。但是到这里,测试就结束了吗?这就天真了,有性能要求就会有性能瓶颈,而我们需要把瓶颈找出来。
于是我开始加大线程数,从40,60,80一直到100,但结果和20的相差不大,并发数再继续加上去也没实际意义,隔壁部门的人数就摆在那。这可肿么办呢?此时可以考虑稳定性,即长时间的压测两个接口,于是调整下设置再来看结果
再加上事务响应时间和TPS
为了提高定位接口报错的效率,可以勾选仅错误日志
摸鱼的时候来看看运行效果,包括聚合报告、TRP、TPS和硬件使用情况,发现持续运行在01:48:00左右开始达到性能拐点,具体表现为TPS下降到很低的数量,但同时响应时间在持续拉长,而CPU在一开始的60%左右竟然跌到了2%,而此时的接口响应时间和错误率也开始拉高,报错内容也是连接数据库超时,事出反常必有妖,停止运行任务,再重复跑了三次,发现拐点时间都差不多,继续定位问题。
再查看硬件的情况,首先get接口所在服务器,不管CPU还是内存的使用率都处于正常
但是post接口所在的服务器,内存在逐渐增加,定位出内存泄漏问题,但是该问题并不是导致瓶颈的元凶。再来分析瓶颈的具体表现,CPU使用率在达到拐点后急速下掉,而且报错内容也是连接数据库超时,这说明到达拐点的时间后,相当于服务器基本没咋处理业务了,那问题肯定出现在到达服务器之前的数据库或者第三方服务,于是把监控方向转移到数据库和第三方服务。
经过开发的各种调试,发现之前引用的索引加错了😱,而且dubbo的线程也跟不上,发版后进行第三轮测试,然而拐点的魔咒依然阴魂不散,继续盯紧数据库和第三方服务,终于捉到了背后元凶——慢查询导致的性能问题。
再经过反复调试和观察后,优化了三个慢查询后,终于结束了本次调优之旅。最终性能表现:9成响应时间在0.24S/2.7S,异常率都为0,每秒平均各能处理57个样本,比之前各多平均处理了50个样本/S,而且支持100个线程长时间的压测,稳定性测试也能满足交付要求。
而服务器在保持稳定高并发的同时,CPU使用率在13%左右,内存使用率相对较低,说明JAVA服务器在硬件方面的利用率较低同时保持了稳定高效的处理时间
本次调优总结:
1.前期线程池太少和缺少索引,同时CPU和内存使用率很低,造成响应时间很长以及高报错率
2.中期测试阶段性能慢主要由于索引问题,加大线程池和加上索引后,接口处理效率从平均每分钟处理55个样本优化到平均每秒处理55个样本,性能方面开始有了质的飞跃,但焰火虽绚烂却短暂,飞跃1个多小时后开始急速下降
3.后期测试阶段主要解决慢查询的问题,由于慢查询导致了中期的性能表现昙花一现,在开发不断的优化和改造下,俩个接口的性能表现又稳又快