1. 性能测试攻略
1.1 微基准性能测试
精准定位到某个模块或者某个方法的性能问题。
适合做功能模块或方法不同实现方式下的性能对比。
1.2 宏基准性能测试
1.2.1 测试环境
-
1.2.2 测试场景
确定在测试某个接口时,是否有其他业务接口同时也在平行运行,造成干扰
-
1.2.3 测试目标
性能测试要有目标,可通过吞吐量及响应时间来衡量是否达标
- 不达标,进行优化
- 达标,继续加大测试并发数,探底接口的TPS,这样可以深入了解到接口性能
- 还要循环测试可能导致性能问题的接口
- 观察各个服务器的CPU、内存及 I/O 使用率变化
1.3 性能测试要注意的问题
1.3.1 热身问题
Java文件编译成 class文件后,机器无法直接执行class文件中的字节码,需要通过解释器将字节码转换成本地机器码,为了节约内存和执行效率,代码最初执行时,解释器先解释执行。 随着代码被执行次数增多,当虚拟机发现某个方法或代码块运行特别频繁,就会认为是热点代码 (Hot Spot Code)。为了提高执行效率,运行时,通过即时编译器(JIT compiler, just-in-time compiler)把这些代码编译成与本地平台相关的机器码,并进行各层次优化,然后存储在内存中,之后每次执行直接从内存中获取。
- 观察各个服务器的CPU、内存及 I/O 使用率变化
在刚开始执行阶段,虚拟机会花费很长时间来全面优化代码,后面可能以最高性能执行。这就是热身过程,如果在性能测试,热身时间过长,会导致第一次访问速度过慢,可以考虑先优化再测试。
1.3.2 性能测试结果不稳定
测试时,伴随着很多不稳定因素
- 机器其他进程影响
- 网络波动
- 每阶段JVM垃圾回收的不同
多次测试,将结果求平均,或者统计一个曲线图,只要保证平均值在合理范围内,波动不大,性能测试算是通过的
1.3.3 多JVM情况下的影响
服务器有多个Java应用服务,部署在不同的Tomcat下,意味着服务器有多个JVM。
任意一个JVM都有整个系统的资源使用权。
应该尽量避免线上环境中一台机器部署多个JVM的情况。
2. 合理分析结果,制定调优策略
测试后,需要输出一份性能测试报告,帮助分析系统性能测试情况。
测试结果需要包含:
- 接口平均、最大和最小吞吐量
- 响应时间
- 服务器的CPU
- 内存
- I/O
- 网络 I/O使用率
- JVM 的 GC 频率等
观察调优标准,发现性能瓶颈,再通过自下而上的方式分析查找问题
- 首先从操作系统层面
- 查看系统的 CPU、内存、I/O、网络使用率是否存在异常
- 通过命令查找异常日志
- 通过分析日志,找到瓶颈原因
- 从Java 应用的 JVM 层面
- 查看 JVM 的垃圾回收频率以及内存分配情况是否存在异常
- 分析日志,找到瓶颈
- 查看应用服务业务层是否存在性能瓶颈
- Java 编程问题
- 读写数据瓶颈等等
2.1 优化代码
应用层问题代码往往会因为耗尽系统资源而暴露出来。
- 如,某段代码导致内存溢出,往往是将JVM 中的内存用完了,这时系统内存资源消耗殆尽,同时也会引发JVM 频繁地发生垃圾回收,导致 CPU 100% 以上居高不下,这时又消耗了系统的CPU资源
非问题代码导致的性能问题比较难发现,需要依靠经验
例如,LinkedList集合使用for循环遍历,将大大降低读的效率,但这种降低很难导致系统性能参数异常。应该使用 Iterator 迭代器循环集合(因为链表)
2.2 优化设计
2.3 优化算法
2.4 时间换空间
2.5 空间换时间
2.6 参数调优
根据业务场景,合理地设置 JVM 的内存空间以及垃圾回收算法可提升系统性能。
如,如果业务中创建大量的大对象,可通过设置,将大对象直接放进老年代,减少年轻代频繁发生小的垃圾回收,减少 CPU 占用,提升性能。
Web 容器线程池的设置以及Linux 系统的内核参数设置不合理也有可能导致系统性能瓶颈,根据自己的业务优化。3. 兜底策略,确保系统稳定性
以上为提高系统性能手段,但无论系统优化得多好,还会存在承受极限,所以为了保证稳定,还需要采用一些兜底策略。
3.1 什么是兜底策略
限流,对系统的入口设置最大访问限制。这里参考 TPS。同时采取熔断措施,友好地返回没有成功的请求。
- 实现智能化横向扩容。可以保证当访问量超过某一个阈值时,系统根据需求自动横向新增服务
- 提前扩容。常用于高并发系统,如抢购业务,因为横向扩容无法满足大量瞬间请求,即使成功了,抢购也结束了
小结
注意:任何调优都需要结合场景明确已知问题和性能目标,不能为了调优而调优,以免引入新的BUG,带来风险和弊端。