性能测试之新一代服务器性能测试工具Gatling
21 世纪是云的世纪, 大规模云网已经出现了,而且在未来几年内会得到高速发展,从而使得基于云的系统也会越来越多。如果要开发一款高性能的云系统,服务器性能测试是一个必不可少的环节 。今天,就来介绍一款新一代服务器性能测试工具 Gatling。
👀
一,什么是 Gatling
Gatling 是一款基于 Scala 开发的高性能服务器性能测试工具,它主要用于对服务器进行负载等测试,并分析和测量服务器的各种性能指标。Gatling 主要用于测量基于 HTTP 的服务器,比如 Web 应用程序,RESTful 服务等 ,除此之外它拥有以下特点:
支持 Akka Actors 和 Async IO,从而能达到很高的性能
支持实时生成 Html 动态轻量报表,从而使报表更易阅读和进行数据分析
支持 DSL 脚本,从而使测试脚本更易开发与维护
支持录制并生成测试脚本,从而可以方便的生成测试脚本
支持导入 HAR(Http Archive)并生成测试脚本
支持 Maven,Eclipse,IntelliJ 等,以便于开发
支持 Jenkins,以便于进行持续集成
支持插件,从而可以扩展其功能,比如可以扩展对其他协议的支持
开源免费 Gatling 适用的场景包括:测试需求经常改变,测试脚本需要经常维护;测试环境的客户机性能不强,但又希望发挥硬件的极限性能;能对测试脚本进行很好的版本管理,并通过 CI 进行持续的性能测试;希望测试结果轻量易读等。
👀
二,Gatling 与 JMeter
JMeter 是目前使用最为广泛的服务器性能测试工具之一,它最大的特点就是拥有一套简单易用的 GUI,但它最大的缺点也是由于简单易用导致它某些方面的不足,比如测试脚本(XML)不容易维护等。Gatling 正是针对 JMeter 的劣势做了大量改进,因此相较于 JMeter,Gatling 拥有以下优势:
在并发性能方面,Gatling 使用了 Akka Actors 和 Async IO, 而 JMeter 则采用了一个用户使用一个线程的方式 ,一旦并发线程过多,性能就急速下降,很难充分发挥硬件的能力。虽然两个工具都是基于 JVM 的,但是 Actors 模型的性能在高并发的情况下性能大大优于 Threads,从而使得 Gatling 在更少的内存和 CPU 的情况下可以提供同样的测试能力,降低了测试成本。图 1 和图 2 分别展现了二者在并发性能方面的表现。
图 1,JMeter 2.8
图 2,Gatling 1.3.2图片,测试环境和测试脚本参见:https://github.com/excilys/gatling/wiki/Benchmarks
其中图 1 和图 2 分别是 JMeter 和 Gatling 在 300 个用户并发下的测试结果。可以很明显的看出,JMeter 的并发量在 300 上下波动,最高达到 400,最低达到 200,而 Gatling 几乎稳定在 300。由此可见 Gatling 性能的稳定性。
在测试脚本方面,Gatling 是 Scala 代码,而 JMeter 主要是 XML 代码。Gatling 基于一套开源的 Gatling DSL API,所以它的功能很容易扩展,也不需要使用者精通 Scala 语言。DSL 的使用也更容易编写出简明,易读性和维护性高的代码,而且还可以使用版本工具进行更有效的管理。因为性能测试应该属于系统发布流程中必不可少的一个步骤,所以测试脚本应该和系统代码一样使用版本工具进行统一管理。
在报表系统上,Gatling 提供了一套轻量并且十分友好的 Html 报表系统,使得用户可以更为快速而方便地查看和分析数据,相反,JMeter 的报表系统却十分笨重,且使用也不方便。
👀
三,如何在项目中使用 Gatling
对于 Gatling 这样一个全新的服务器性能测试工具,是否能将它很好的运用到项目中并发挥其优势,这个是一个困扰测试决策者的问题 。下面我将结合在一个真实项目中使用和部署 Gatling 的经验来解答这个问题。
1,搭建测试环境
在一个大型的 Web 项目中,测试环境的搭建是项目测试工作开始的第一步,也是最为关键的一步,因为测试环境直接影响到测试成本和测试结果。由于这个项目对于性能的要求并不是很高,我们经过讨论和分析,决定选取虚拟机作为测试平台。这就意味着被测系统以及测试客户端可以使用的硬件资源比如 CPU 和内存十分有限,因此需要测试工具能充分使用有限的资源发挥最大的性能。
2,进行负载测试
为了快速实现测试脚本,我首先选择了使用 Gatling 录制功能进行脚本录制,成功录制以后会在指定的“Output folder”目录下面生成你指定“Class Name”为名字的脚本,见图 3。
图 3,Gatling Recorder
根据图 3 的配置,录制好的脚本存放在/Users/twer/work/gatling/user-files/simulations/Common/MyRecordedSimulation.scala。生成的部分脚本代码如下:
class MyRecordedSimulation extends Simulation { val httpProtocol = http .baseURL("http:// :10.17.7.3") .acceptHeader("image/png,image/*;q=0.8,*/*;q=0.5") .acceptEncodingHeader("gzip, deflate") .acceptLanguageHeader("en-US,en;q=0.5") .connection("keep-alive") .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:22.0) Gecko/20100101 Firefox/22.0") val headers_1 = Map( """Accept""" -> """text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8""", """If-None-Match""" -> """"a3ef335152d5532e2297bd8ad288f3f9"""")
录制代码段 1
.exec(http("request_21")
.get("""/customer/images/new?app_dialog=true&dialog=true""")
.headers(headers_18))
.pause(6)
.exec(http("request_22")
.get("""/customer/images?_=1374400463122""")
.headers(headers_19))
.pause(165 milliseconds)
.exec(http("request_23")
.get("""/Users/twer/work/gatling/user-files/simulations/testdata/test1.png""")
.headers(headers_20)
.check(status.is(500)))
.pause(2)
.exec(http("request_24")
.get("""/customer/images?view=list"""))
.pause(86 milliseconds)
……
setUp(scn.inject(atOnce(1 user))).protocols(httpProtocol)
录制代码段 2
录制出来的脚本拥有很多局限性:
只支持 1 个用户
没有检测点
没有逻辑分层
因此,它并不能用于真正的性能测试。对于这样的原始代码,我们需要进行大量的重构,使代码拥有很好的可读性和可维护性。
首先我们要进行分层处理:
对于录制代码段 1,需要建立一个 Header 类来管理所有 HTTP Header,这里使用“Headers.scala”,在录制代码段 1 中只给出了“headers_1”,实际的脚本包含了大量的 Header。
对于录制代码段 2,需要将测试场景和测试控制分开,每一个测试场景使用一个文件来保存, 代码段 2 所示的场景使用“UploadImageScenario.scala”来保存。主控脚本也需要分离出来存入“MainSimulation.scala”,通过调用不同的测试场景的脚本,从而可以复用 HTTP 的配置选项,比如:
val httpProtocol = http .baseURL("http:// :10.17.7.3")
其次,我们还需要增加多用户的支持:
多用户数据的读入,其中“user_credentials.csv”存储的就是用户名和密码
.feed(csv("user_credentials.csv")) .exec(http("request_login") .post("""/customer/login""") .param("""username""", """${username}""") .param("""password", "${password}""")
设置多用户的值。由于我们使用的是虚拟机,所以经过测试,确定为 400 用户并发。
setUp(LoginScenario.loginScn.inject(ramp(400 users) over(60 seconds))).protocols(httpProtocol)
最后,我们还要增加检测点,使用 check,find,status 等函数进行检测,下面的代码检测了用户登出的时候 HTTP Response Status 是否为 302:
exec(http("request_logout") .get(("""/customer/logout""") .headers(headers_logout) .check(status.is(302)))
当然,如果测试人员熟悉 Gatling DSL API,我们也可以不用录制代码再进行重构,而是直接设计测试系统并进行测试案例的开发。
项目采取了敏捷方法进行开发,所以系统的一些功能在开发过程中会出现频繁改动,导致测试场景和测试脚本也会随之发生改变,因此,测试脚本的可读性和可维护性对于我们来说就非常重要。当某个功能改变之后,使用 Gatling 脚本就能十分方便的进行阅读和重构。比如对于添加 user 的功能,第一版只需要能添加 user 即可(见添加 user 代码 1),而在下一版中,则要求在添加 user 时可以选择该 user 具有那些权限(见添加 user 代码 2),代码如下:
.exec(http("request_add_user") .post("""/customer/users""") .headers(headers_user) .param("""utf8""", """✓""") .param("""user[username]""", """user2""") .param("""user[email]""", """user@gmail.com""") .param("""user[password]""", """user2""") .param("""user[password_confirmation]""", """user2""")
添加 user 代码 1
.exec(http("request_add_user") .post("""/customer/users""") .headers(headers_user) .param("""utf8""", """✓""") .param("""user[username]""", """user2""") .param("""user[email]""", """user@gmail.com""") .param("""user[password]""", """user2""") .param("""user[password_confirmation]""", """user2""") .param("""user[plugins][]""", """customer_dashboard""") .param("""user[plugins][]""", """customer_files""") .param("""user[plugins][]""", """customer_images""") .param("""user[plugins][]""", """customer_pages"""))
添加 user 代码 2
项目发布后,若项目功能发生改变,我们也可以使用 Gatling 进行持续的性能回归测试,保证系统性能不会因为某次修改导致非预期的降低。如果降低了,就要进行及时的调查,修复或者是调整,保证性能一直在预期的可控范围内。
3,测试报表
Gatling 测试报表基于 HTML,并且在测试过程中业已生成,所以打开速度很快。而且,当把鼠标移动到不同数据轴上时,都会有弹出框显示这个点上详细的测试数据信息。这种动态显示数据的方式非常方便查看和分析数据。考虑到项目真实数据的不便,我将通过 Gatling 官方网站给出的示例报表进行说明。
Gatling 的报表分为两类:GLOBAL 和 DETAILS,其中 GLOBAL 主要是请求相关的统计数据,比如每秒请求数,请求成功与失败数等;其中 DETAILS 主要是请求时间相关的统计数据,比如请求响应时间,请求响应延迟时间等。
图 4 每秒请求数,图片来自 http://gatling-tool.org/sample/req_home.html
当鼠标放到图中任何一个点的时候,对应时间点上请求的详细数据就会以图中白色的弹出框的方式进行显示。在下面的请求响应延迟时间图里面也有同样的功能。
图 5 请求响应延迟时间,图片来自 http://gatling-tool.org/sample/index.html
4,与 CI 的集成
项目的 CI 系统选用的是 Jenkins,因为 Jenkins 有 Gatling 的插件,所以通过这个插件可以在 Jenkins 上直接查看 Gatling 的测试结果,如图 6 所示。
图 6 Jenkins Gatling 插件,图片来自:https://wiki.jenkins-ci.org/display/JENKINS/Gatling+Plugin
我们还把生成的报表存档到每个 Build 里面,这样就可以在每个 Build 中获得那次测试的所有报表。
5,更多类型的测试
其他类型的 HTTP 服务器性能测试,比如瞬间压力测试,耐久性测试等, 都十分适合使用 Gatling。
👀
四,未来的 Gatling
Gatling 发布的时间虽然不长,但凭借其优良的性能,DSL 模式的脚本,轻量友好的报表系统在众多服务器性能测试工具中脱颖而出。在 2013 年 5 月发布的 ThoughtWorks 技术雷达中,Gatling 被列入了 ADOPT,并在一些 ThoughtWorks 项目中得到了实际的运用。不过,Gatling 还是存在一些问题,比如不支持分布式模型;默认只支持 HTTP,对于其他协议需要自己动手进行扩展;报表种类也不是很丰富 。倘若 Gatling 能在这几方面有所突破,那么它必将成为新一代服务器性能测试工具中的杀手锏。
测试开发栈
软件测试开发合并必将是趋势,不懂开发的测试、不懂测试的开发都将可能被逐渐替代,因此前瞻的技术储备和知识积累是我们以后在职场和行业脱颖而出的法宝,期望我们的经验和技术分享能让你每天都成长和进步,早日成为测试开发栈上的技术大牛~~
长按二维码/微信扫描关注
互联网测试开发一站式全栈分享平台