面向开发的测试技术(二):性能测试

引子:自上世纪末Kent Beck提出TDD(Test-Driven Development)开发理念以来,开发和测试的边界变的越来越模糊,从原本上下游的依赖关系,逐步演变成你中有我、我中有你的互赖关系,甚至很多公司设立了新的QE(Quality Engineer)职位。和传统的QA(Quality Assurance)不同,QE的主要职责是通过工程化的手段保证项目质量,这些手段包括但不仅限于编写单元测试、集成测试,搭建自动化测试流程,设计性能测试等。可以说,QE身上兼具了QA的质量意识和开发的工程能力。我会从开发的角度分三期聊聊QE这个亦测试亦开发的角色所需的基本技能,这篇是第二篇。

前情概要:

1 什么是性能测试?

先来看一下维基百科里对性能测试的定义,

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. - Wikipedia

注意上述定义中有三个关键词:

  • responsiveness,即响应时间,请求发出去之后,服务端需要多久才能返回结果,显然响应时间越短,性能越好。
  • stability,即稳定性,同样的请求,不同时刻发出去,响应时间差别越小,稳定性越好,性能也越好。
  • workload,即负载,同一时刻服务端收到的请求数量,其中单位时间内成功处理的请求数量即吞吐量,吞吐量越大,性能越好。

响应时间和吞吐量是衡量应用性能好坏最重要的两个指标。对于绝大多数应用,刚开始的时候,响应时间最短;随着负载的增大,吞吐量快速上升,响应时间也逐渐变长;当负载超过某一个值之后,响应时间会突然呈指数级放大,同时吞吐量也应声下跌,应用性能急剧下降,整个过程如下:

图片出处:性能测试应该怎么做?

2 性能测试的目的

了解了应用性能变化的普遍规律,性能测试的目的也就有了答案:针对某一应用,找出响应时间和吞吐量的量化关系,找到应用性能变化的临界点。你可能会问,知道了这些有什么用呢?在我看来,至少有3个层面的好处:

第一,有的放矢,提高资源利用率。性能测试的过程就是量化性能的过程,有了各种性能数据,你才能对应用性能进行定量分析,找到并解决潜在的性能问题,从而提高资源利用率。

第二,科学的进行容量规划。找到了应用性能变化的临界点,也就很容易找到单节点的性能极限,这是进行容量规划的重要决策依据。比如某一应用在单节点下的极限吞吐量是2000 QPS,那么面对10000 QPS的流量,至少需要部署5个节点。

第三,改善QoS(Quality of Service)。很多时候,资源是有限的,面对超出服务能力的流量,为了保证QoS,必须做出取舍(比如限流降级,开关预案等),应用性能数据是设计QoS方案的重要依据。

3 性能测试的三个常见误区

误区1:只看平均值,不懂TP95/TP99

用平均值来衡量响应时间是性能测试中最常见的误区。从第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,问题就很清楚了。

误区2:只关注响应时间和吞吐量,忽视请求成功率

虽说衡量应用性能好坏最主要是看响应时间和吞吐量,但这里有个大前提,所有请求(如果做不到所有,至少也要绝大多数请求,比如99.9%)都被成功处理了,而不是返回一堆错误码。如果不能保证这一点,那么再低的响应时间,再高的吞吐量都是没有意义的。

误区3:忘了测试端也存在性能瓶颈

性能测试的第三个误区是只关注服务端,而忽略了测试端本身可能也存在限制。比如测试用例设置了10000并发数,但实际运行用例的机器最大只支持5000并发数,如果只看服务端的数据,你可能会误以为服务端最大就只支持5000并发数。如果遇到这种情况,或者换用更高性能的测试机器,或者增加测试机器的数量。

4 如何进行性能测试?

介绍完性能测试相关的一些概念之后,再来看一下有哪些工具可以进行性能测试。

4.1 JMeter

JMeter可能是最常用的性能测试工具。它既支持图形界面,也支持命令行,属于黑盒测试的范畴,对非开发人员比较友好,上手也非常容易。图形界面一般用于编写、调试测试用例,而实际的性能测试建议还是在命令行下运行。

image.png

并发设置

image.png

请求参数

image.png

结果报表

命令行下的常用命令:

  • 设置JVM参数:JVM_ARGS="-Xms2g -Xmx2g"
  • 运行测试:jmeter -n -t <jmx_file>
  • 运行测试同时生成报表:jmeter -n -t <jmx_file> -l <log_file> -e -o <report_dir>

除了JMeter,其他常用的性能测试工具还有ab, http_load, wrk以及商用的LoaderRunner

4.2 JMH

如果测试用例比较复杂,或者负责性能测试的人员具有一定的开发能力,也可以考虑使用一些框架编写单独的性能测试程序。对于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();
    }
}

其中:

  • @BenchmarkMode: 性能测试模式,支持Throughput,AverageTime,SingleShotTime等多种模式。
  • @Fork: 设置运行性能测试的Fork进程数,默认是0,表示共用JMH主进程。
  • @Threads: 并发数,Threads.MAX表示同系统的CPU核数。
  • @Warmup和@Measurement: 分别设置预热和实际性能测试的运行轮数,每轮持续的时间等
  • @Setup和@Benchmark: 等同于JUnit里的@BeforeClass和@Test

在命令行下,使用JMH框架编写的性能测试程序只能以Jar包的形式运行(Main函数固定为org.openjdk.jmh.Main),因此一般会针对每个JMH程序单独维护一个项目。如果是Maven项目,可以使用官方提供的jmh-java-benchmark-archetype,如果是Gradle项目,可以使用jmh-gradle-plugin插件。

4 小结

以上就是我对性能测试的一些见解,欢迎你到我的留言板分享,和大家一起过过招。下一篇我将聊一下Web的自动化测试,敬请期待。

5 参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 本文翻译自 Thinking Clearly About Performance 这是我三年前读到的一篇关于性能问...
    mindwind阅读 1,279评论 1 8
  • 主要文体来自 CDNS:https://www.cnblogs.com/ceshisanren/p/5639895...
    Amano阅读 10,960评论 3 27
  • 在使用Jmeter进行接口的性能测试时,由于Jmeter 是JAVA应用,对于CPU和内存的消耗比较大,所以,当需...
    燕京博士阅读 4,158评论 0 16
  • 1. JMeter 测试计划 测试计划 使用JMeter进行测试的起点,是其它JMeter测试元件的容器。 线程组...
    alamZheng阅读 3,339评论 0 7
  • js计时器 time{ } mytime{ } dd{ } 代码已完毕有不懂联系我。
    李仁平阅读 7,032评论 0 3