压测经历2 ——模拟真实情况
经历1里面只是对单台测试,后面我们想看看整个集群的qps。然后发现了很多问题。。。。
一.改为随机数测试
用户画像的数据大概有3亿,我想模拟真实访问情况,于是通过random函数随机取用户id,进行查询。测试数据如下:
并发数 | 响应时间 | 理论qps | 实际qps |
---|---|---|---|
10 | 1.7ms | 5.8k/s | 3.2k/s |
50 | 3.5ms | 14.3k/s | 8.6k/s |
100 | 6.7ms | 14.9k/s | 9.6k/s |
通过上面的数据可以发分析得出两个结果:
- 理论qps和实际qps不匹配,且偏小,说明测试机器的响应时间比监控的时间要大,这里面主要有两个因素影响,网络传输和测试机器的处理能力。
- 并发数增加,响应时间成倍增加,说明服务端也存在性能瓶颈。
发现问题,就开始解决问题,首先解决问题2。
二.性能瓶颈
发现性能问题后,首先想到的是CPU、内存、I/O和网络是否存在瓶颈。还好公司基本上所有的服务都接入了grafana(感谢运维大大们),打开网页看了一下除了在100个并发的时候cpu达到了50%,其他的资源利用率都很低。下图是我测试了一天的的效果图。
这时候只能从程序本身去找问题了,也是两个方向:
- jvm是否正常,
- review代码,看是否有特别耗时的地方。
(1)GC情况
之前的jvm的参数都是从别的地方拷贝过来,可以通过参数
-XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=10M -Xloggc:/data/bdi.profile/logs/gc_20171027_140757.log
打印gc运行日志,统计如下报表:
并发数 | ygc 耗时 | 间隔 |
---|---|---|
10 | 20ms | 400ms |
20 | 20ms | 200ms |
100 | 20ms | 135ms |
eden区的gc时间一直很稳定,但是间隔时间变短了。说明并发数上来,gc变的更频繁了。eden的分配的空间为64M,分配的比较小,因为我们的请求生成的对象大部分都是eden区就被gc了。不过空间太小,导致频繁gc,会增大请求响应时间,自然需要扩大新生代的大小了。而且从我们的日志观测来看,隔一段时间,响应时间会变的很大,主要原因是CMS在gc过程中会产生碎片,等达到一定阈值,会集中回收。
所以后续直接使用G1的方式回收,初始堆大小分配了8g。分配如下:
-Xms8g -Xmx8g -XX:+UseG1GC -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:G1RSetUpdatingPauseTimePercent=5 -XX:InitiatingHeapOccupancyPercent=70 -XX:ParallelGCThreads=5 -XX:ConcGCThreads=5 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=10M -Xloggc:/data/bdi.profile/logs/gc_20171027_140757.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/bdi.profile/tmp/heapdump_20171027_140757.hprof -Djava.io.tmpdir=/data/bdi.profile/tmp/ -Duser.dir=/data/bdi.profile -DTEAM=bdi -DAPPID=bdi.profile -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=18089 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -classpath /data/bdi.profile/conf:/data/bdi.profile/lib/*: me.ele.core.container.Container
本以为改完后,性能会有质的飞升,然而qps并没有上来很多多,jvm最多有20%的影响,当然是在我们并发数低的情况下。
(2)review代码
其实查询逻辑很简单,用户的请求来了,先校验权限,然后首先去缓存中查询,如果没命中就到ekv中查询。把所有可能出现耗时的地方都用日志打印了一遍。这个过程也是蛮痛苦的,因为数据量比较大,只能发布到线上才能测试。然后就这样一遍遍的查询,最后也并没有找到什么问题。。。。。最后都快放弃了,然后有一个同事跟我说你把log关了试试,其实我之前也考虑到日志会影响性能,虽然我的日志级别是info,但是之前代码中一行数据都没有写到file中。后来报的试试的态度,把日志级别的态度调到了erro,然后。。。qps就怎么上来了,大概达到4w的时候,cpu使用率快达到80%
至此总算压倒机器的性能瓶颈了。关于log的工具,是公司基于log4j封装的一个jar包,默认会记录很多其他信息,及时你不往文件里面,但是只要是info级别,它也会写很多信息。