引子:自上世纪末Kent Beck提出TDD(Test-Driven Development)开发理念以来,开发和测试的边界变的愈来愈模糊,从本来上下游的依赖关系,逐步演变成你中有我、我中有你的互赖关系,甚至不少公司设立了新的QE(Quality Engineer)职位。和传统的QA(Quality Assurance)不一样,QE的主要职责是经过工程化的手段保证项目质量,这些手段包括但不只限于编写单元测试、集成测试,搭建自动化测试流程,设计性能测试等。能够说,QE身上兼具了QA的质量意识和开发的工程能力。我会从开发的角度分三期聊聊QE这个亦测试亦开发的角色所需的基本技能,这篇是第二篇。html
前情概要:java
先来看一下维基百科里对性能测试的定义,git
In software engineering, performance testing is in general, a testing practice performed to determine how a system performs in terms of responsiveness and stability under a particular workload. - Wikipediagithub
注意上述定义中有三个关键词:spring
响应时间和吞吐量是衡量应用性能好坏最重要的两个指标。对于绝大多数应用,刚开始的时候,响应时间最短;随着负载的增大,吞吐量快速上升,响应时间也逐渐变长;当负载超过某一个值以后,响应时间会忽然呈指数级放大,同时吞吐量也应声下跌,应用性能急剧降低,整个过程以下:shell
图片出处:性能测试应该怎么作?apache
了解了应用性能变化的广泛规律,性能测试的目的也就有了答案:针对某一应用,找出响应时间和吞吐量的量化关系,找到应用性能变化的临界点。你可能会问,知道了这些有什么用呢?在我看来,至少有3个层面的好处:segmentfault
第一,有的放矢,提升资源利用率。性能测试的过程就是量化性能的过程,有了各类性能数据,你才能对应用性能进行定量分析,找到并解决潜在的性能问题,从而提升资源利用率。架构
第二,科学的进行容量规划。找到了应用性能变化的临界点,也就很容易找到单节点的性能极限,这是进行容量规划的重要决策依据。好比某一应用在单节点下的极限吞吐量是2000 QPS,那么面对10000 QPS的流量,至少须要部署5个节点。并发
第三,改善QoS(Quality of Service)。不少时候,资源是有限的,面对超出服务能力的流量,为了保证QoS,必须作出取舍(好比限流降级,开关预案等),应用性能数据是设计QoS方案的重要依据。
用平均值来衡量响应时间是性能测试中最多见的误区。从第1小节的插图能够看出,随着吞吐量的增大,响应时间会逐渐变长,当达到最大吞吐量以后,响应时间会开始加速上升,尤为是排在后面的请求。在这个时刻,若是只看平均值,你每每察觉不到问题,由于大部分请求的响应时间仍是很短的,慢请求只占一个很小的比例,因此平均值变化不大。但实际上,可能已经有超过1%,甚至5%的请求的响应时间已经超出设计的范围了。
更科学、更合理的指标是看TP95或者TP99响应时间。TP是Top Percentile的缩写,是一个统计学术语,用来描述一组数值的分布特征。以TP95为例,假设有100个数字,从小到大排序以后,第95个数字的值就是这组数字的TP95值,表示至少有95%的数字是小于或者等于这个值。
以一次具体的性能测试为例,
总共有1000次请求,平均响应时间是58.9ms,TP95是123.85ms(平均响应时间的2.1倍),TP99是997.99ms(平均响应时间的16.9倍)。假设应用设计的最大响应时间是100ms,单看平均时间是彻底符合要求的,但实际上已经有超过50个请求失败了。若是看TP95或者TP99,问题就很清楚了。
虽然说衡量应用性能好坏最主要是看响应时间和吞吐量,但这里有个大前提,全部请求(若是作不到全部,至少也要绝大多数请求,好比99.9%)都被成功处理了,而不是返回一堆错误码。若是不能保证这一点,那么再低的响应时间,再高的吞吐量都是没有意义的。
性能测试的第三个误区是只关注服务端,而忽略了测试端自己可能也存在限制。好比测试用例设置了10000并发数,但实际运行用例的机器最大只支持5000并发数,若是只看服务端的数据,你可能会误觉得服务端最大就只支持5000并发数。若是遇到这种状况,或者换用更高性能的测试机器,或者增长测试机器的数量。
介绍完性能测试相关的一些概念以后,再来看一下有哪些工具能够进行性能测试。
JMeter多是最经常使用的性能测试工具。它既支持图形界面,也支持命令行,属于黑盒测试的范畴,对非开发人员比较友好,上手也很是容易。图形界面通常用于编写、调试测试用例,而实际的性能测试建议仍是在命令行下运行。
并发设置
请求参数
结果报表
命令行下的经常使用命令:
除了JMeter,其余经常使用的性能测试工具还有ab, http_load, wrk以及商用的LoaderRunner。
若是测试用例比较复杂,或者负责性能测试的人员具备必定的开发能力,也能够考虑使用一些框架编写单独的性能测试程序。对于Java开发人员而言,JMH是一个推荐的选择。相似于JUnit,JMH提供了一系列注解用于编写测试用例,以及一个运行测试的引擎。事实上,即将发布的JDK 9默认就会包含JMH。
下面是我GitHub上的示例工程里的一个例子,
@BenchmarkMode(Mode.Throughput)
@Fork(1)
@Threads(Threads.MAX)
@State(Scope.Benchmark)
@Warmup(iterations = 1, time = 3)
@Measurement(iterations = 3, time = 3)
public class VacationClientBenchmark {
private VacationClient vacationClient;
@Setup
public void setUp() {
VacationClientConfig clientConfig = new VacationClientConfig("http://localhost:3000");
vacationClient = new VacationClient(clientConfig);
}
@Benchmark
public void benchmarkIsWeekend() {
VacationRequest request = new VacationRequest();
request.setType(PERSONAL);
OffsetDateTime lastSunday = OffsetDateTime.now().with(TemporalAdjusters.previous(SUNDAY));
request.setStart(lastSunday);
request.setEnd(lastSunday.plusDays(1));
Asserts.isTrue(vacationClient.isWeekend(request).isSuccess());
}
// 仅限于IDE中运行
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(VacationClientBenchmark.class.getSimpleName())
.build();
new Runner(opt).run();
}
}复制代码
其中:
在命令行下,使用JMH框架编写的性能测试程序只能以Jar包的形式运行(Main函数固定为org.openjdk.jmh.Main),所以通常会针对每一个JMH程序单独维护一个项目。若是是Maven项目,可使用官方提供的jmh-java-benchmark-archetype,若是是Gradle项目,可使用jmh-gradle-plugin插件。
以上就是我对性能测试的一些看法,欢迎你到个人留言板分享,和你们一块儿过过招。下一篇我将聊一下Web的自动化测试,敬请期待。