一, 性能测试为什么要成为一等公民

敏捷开发在当今软件行业里面扮演着越来越重要的角色,软件测试也随着逐步敏捷起来。由于软件系统,特别是服务器系统越来越复杂,规模也越来越大。开发人数也达到几十,甚至几百人,而且大规模使用第三方的软件库,比如Spring,Rails,Hibernate,.Net等。如果使用不当,将会引起很严重的性能问题甚至是稳定性问题,所以性能问题在当前的软件开发中已经越来越明显了。常规的持续集成验证了构建是否满足了功能设计要求,而持续性能测试增加了另外一重验证标准,程序是否满足了性能要求,从而是性能问题尽早被发现。持续性能测试主要的优点就是可以在代码改变以后可以快速的知道性能变化,比如如果发现性能问题,可以让提交这个Commit程序员去修复这个问题,因为他还能记住这个Commit。如果等到最后发布之前才发现,那么这个程序员可能已经不记得这个Commit,或者已经离开了公司,有可能导致修复这个问题的难度大大增加。如果这个是一个设计上的错误,那么团队就可以尽早修复它并避免以后的功能受其影响。

比如铁道部的12306购票系统上线后的第一个春节就遇到了严重的性能问题,面对预料中的高访问量,系统在春运期间经常长时间无法访问,导致大量用户无法购票。像这样严重的性能问题是在开发的时候是可以预见的,不过还是出现在产品环境上,由此可见系统在构架上没有对性能进行有效的设计,在测试上没有进行有效的性能测试。当这个性能问题出现的时候,根本无法在短时间内修复,导致了如此严重的性能问题维持了很长一段时间。在第二年的春运里面,系统才增加了排队系统,有效的缓解了性能问题,不过还是会时不时出现无法访问的情况。由于12306是中国唯一的网上火车票购票系统,就算出现这样严重的性能问题,人们还是只有继续使用它。但是如果有多个购票网站,一旦某个出现了性能问题而导致客户长时间无法访问,那么就会带来大量的客户流失,造成巨大的损失。因此性能测试对拥有大量用户软件系统十分重要,而且需要越早发现性能问题,越早修复越好,因为等到发布前,就算测试出性能问题,也有可能因为构架问题而无法修复。

让性能测试成为敏捷开发的一等公民对于更好的进行敏捷开发和高质量的持续部署越来越重要。持续性能测试不应该只是说说,特别是对于大型服务器项目和开发人员众多的情况下,持续性能测试将成为必不可少的组成部分。持续性能测试应该被看做持续交付的重要步骤,应该和回归测试一样,可以做到更频繁的高性能持续交付。

二,当前性能测试存在的问题

当前软件行业里面普遍还是有在做性能测试,不过或多或少都存在这样那样的问题,从而导致性能测试不能成为一等公民,无法有效的让性能测试来为软件开发和持续部署服务,总的说来存在以下一些问题:

1,一般性能测试都会在某个Release或者Milestone之前进行测试

我经历过很多个项目:运营商计费系统项目,社交网络项目,金融项目等,其性能测试一般都是某个Release或者某个Milestone发布前进行测试。这样遇到的问题就是如果测试结果很差,然后就需要来进行分析调查和优化,从而导致在发布推迟,或者可能根本无法修复这个问题。但是有可能引发性能变差的就是某一个或者某几个Commit。那么为什么我们不能在这样的Commit提交之后就及时发现它,及时修复它。一般来讲,软件的问题越晚发现,修复的成本就越高。因为分析和解决这个问题需要更长的时间,比如引入这个问题的人已经离开项目组了,还有因为项目的代码已经比引入问题的时候复杂了很多倍,从而导致修复变得困难。

2,对于已经发布的软件在维护阶段 一般很少做性能测试

在我经历的很多项目中,在维护阶段一般不做或者很少做性能回归测试。因为很多人觉得维护的时候没有必要做,性能回归测试成本高,并且维护的时候进行功能测试保证功能正常就可以了,但是这个往往是一个误区。越庞大的项目,维护就越难,并且维护人员一般都不是开发时候的主力人员,所以很少有人能从全局上看清楚整个项目,导致进行Bug修复或者其他维护修改的时候不能保证没有降低系统性能。所以对于越大的项目,性能回归测试越重要。

3,脚本繁琐,重用度低

由于现在常用的性能测试工具的测试脚本不易理解,也不易维护,不方便Review,脚本本身质量难以提高,基本无法重用,导致很少人读脚本,性能测试的知识在团队里传递的慢。其原因包括:

  • 要么是专有脚本不易学习理解,
  • 要么就是不易读的XML不易维护
  • 要么图形操作不易维护 比如当前行业里面最常用的两个性能测试工具:
  • JMeter的Test Plan是基于XML的,所以很难进行维护和修改,只能通过其提供的GUI进行开发和修改。但是正是由于这种GUI的方式导致维护更加麻烦,特别是测试逻辑十分复杂的时候,而且也很难对这样的脚本进行版本管理。
  • Loadrunner使用自己专有的脚本语言,学习曲线比较大,而且无法重用开发者以前的语言技能。

4,测试工具本身的性能也不好,从而导致测试成本较高,测试不稳定

很多性能测试工具的性能不高,从而使得达到特定测试条件需要的硬件很多,测试时间很长。提高性能测试工具自己的性能是非常有必要的,可以达到降低测试成本和测试时间。比如JMeter采用了一个用户使用一个线程的方式 ,一旦并发线程过多,性能就急速下降,很难充分发挥硬件的能力,其分布式方案性能也十分不理想,很难达不到预期的测试效果。

5,很难准备有效的测试数据

由于性能测试和功能测试不同,它需要十分大量的有效的测试数据。比如如果要测试12306这样的系统,就要构造足够多的不同IP,不同浏览器以及不通的用户信息。不过现在一般性能测试都是单一的递增模拟,或者简单的随机模拟,这样一般都很难模拟出真实的情况,从而导致无法获得有效的测试结果,漏测了一些情况。所以针对系统的业务情况和真实的使用环境,系统的准备大量的有效数据是十分重要的。

6,性能测试工具种类众多,选择困难

由于现在性能测试工具太多,商用的价格太贵,而且不具备通用性,一旦使用就很难更换。但是对于开源测试软件又是五花八门,所以怎么选择测试工具,也是一个难题。

7,其他问题:

  • 长时间的性能测试方案分析和论证
  • 繁琐的环境准备
  • 测试过程跟开发者关系不大
  • “该” 性能测试了,自己到网上搜一番,别人怎么测我怎么测
  • 脚本写完了,跑完了,报告交了,谁知道下次性能测试什么时候做,谁来做
  • 各种用户问题:响应时间长,页面加载慢,失败率高…
  • 从性能问题被引入到被发现的时间周期长
  • “你报告的问题都对,但现在已经没时间修了,只能上线”
  • “修这种问题影响太大,需要架构师、经理的review”
  • 病入膏肓,只能依靠专家,要有“性能测试专家”

三,一等公民的性能测试,它应该拥有那些特点

1,能很好的与敏捷开发和持续集成有效结合

对于敏捷开发必不可少的就是CI, 比如Jenkins。那么作为一等公民的性能测试,也必须能很好的与CI进行集成。

  • 为了将性能测试加入到敏捷开发的CI里面,我们首先必须定义测试阀门(测试目标),比如于服务器系统的性能测试就是服务器的Response / Latency Time。需要有一个这样的阀门,当性能测试的结果低于这个阀门就应该阻止pipeline进入下一个步骤,然后返回来分析性能为什么会低于这个预置阀门。
  • 保证每个功能开发以后或者每天的工作之后都进行性能测试,确保性能没有降到预设指标之下。这样就可以在最第一时间发现性能的下降或者性能问题的出现。那我们为什么不为每个Commit都做性能测试啦?因为有一些小的Commit,比如添加一些注视,或者添加一些测试就没有必要进行性能测试了。所以需要开发人员在提交测试的时候需要给一个明确的Tag,比如Performance,Complete等,让CI认识到当前这次集成需要进行性能测试,从能把性能测试用到真正有意义的地方。
  • 如果性能测试发现问题,应向对待Bug一样进行处理:分析测试数据,找到Root Cause,然后尝试修复,如果修复不了,比如新的功能就一定会降低性能,那么就只有降低预设阀门。但是在这个阶段进行调整,比到了某个Release或者Milestone的时候来做成本将会低很多,也容易很多。

下图为某个项目中包含持续性能测试的Jenkins构建管道图:

Jenkins 构建管道

除此之外还需要很好的和敏捷开发流程进行集成,将性能测试的Story放在相应的功能Story之后进行开发,做到持续性能测试开发:

敏捷开发流程中的性能测试

2,易读并且易维护的测试脚本

对于测试脚本而言,如果能使用一个通用的编程语言(Ruby, Python, Scala等)而不是专有语言或者DSL进行开发,那么将使得脚本的开发、维护更加便利和快速。特别是如果能使用DSL,那么将更加易读与维护。从性能测试脚本开发的角度来说,不再依赖丑陋过时的GUI,不需要再去阅读大段大段的录制脚本。业务逻辑和测试逻辑一目了然,方便重用,重构和团队合作,

3,更为直观和易读的测试报表

拥有一个很好的CI插件(比如Jenkins的插件),能很容易的配置各种测试参数,以及方便的解析测试结果数据,并能自定义检测标准,从而使性能测试能正真意义上的加入到敏捷开发的自动化流程里面。下图是Jenkins中一个性能测试的测试结果报表:

Jenkins性能测试报表

4,时机与方法

  • 尽早且持续的性能测试,设置并持续改进性能指标
  • 不要怕小,不要怕简单,先跑起来,建模从简,持续改进,问题域少,各个击破,成为持续集成的一部分
  • 快速且持续的反馈,问题从引入到发现的时间:最少几分钟,最多一周
  • 而对于持续交付,性能测试将成为发布之前必不可少的一个检测步骤,从而保证持续交付出去的软件在性能上更为稳定

四,尽可能的利用“监测”代替“测试”

在产品环境中,如果想知道用户从哪来,用户的在线时间怎样,TPS如何分布,响应时间和负载的关系,都可以从客户端和服务器端的监测工具中取得,而且是——精确的。所以在产品环境中使用“监测”代替“测试”,可以获取到详尽的监测数据,并且可以帮助我们进行产品环境下的瓶颈分析。

监测数据样图

五,总结

让性能测试成为敏捷开发的一等公民对于更好的进行敏捷开发和高质量的持续部署越来越重要。在2013年5月发布的ThoughtWorks技术雷达中,让性能测试成为敏捷开发一等公民已经被列入了ADOPT。对于当今的软件系统,特别是对于大型服务器系统,并且它又拥有大量用户的情况下,持续性能测试将成为一个必不可少的组成部分。让我们一起去实践持续性能测试,比如新一代的性能测试工具Gatling就是一个很好的试验田,通过它,我们可以很好的实践对于服务器系统的持续性能测试。