使用Gatling作web压力测试遇到的一些状况

Gatling是什么

Gatling是一个使用Scala编写的开源的负载测试框架,基于Akka和Netty,具备如下亮点:css

  • 高性能
  • 友好的HTML报告
  • 基于情境的记录器(recoder),对开发友好的DSL

Gatling VS Jmeter

Jmeter是目前很是成熟的负载测试工具,支持至关多的协议,支持插件,能够轻松的扩展。html

而Gatling性能上更有优点,而且使用Scala DSL代替xml作配置,相比jmeter要更灵活,并且更容易修改和维护。git

关于Jmeter和Gatling的一个比较好的对比能够参见infoq的文章github

同时,Gatling也对MavenGradle这样的构建工具比较友好,易于集成到Jenkins中,轻松加入到CI流程中。编程

TIPS: 在实际使用中建议版本化管理gatling的配置,使用maven插件gradle插件造成对应的maven/gradle工程项目管理,更容易,并且容量更小,升级gatling也会更方便,减小了不少手工的操做。api

Gatling的基本使用

从官方网站下载zip压缩包,解压就好了,须要预先安装有JDK,并设置好JAVA_HOME,熟悉JAVA的朋友应该都懂,就不细说了。session

Gatling的目录结构看起来像这样:架构

LICENSE │ ├─bin │ gatling.bat │ gatling.sh │ recorder.bat │ recorder.sh │ ├─conf │ gatling-akka.conf │ gatling.conf │ logback.xml │ recorder.conf │ ├─lib ├─results │ .keep │ └─user-files ├─bodies │ .keep │ ├─data │ search.csv │ └─simulations └─computerdatabase │ BasicSimulation.scala │ └─advanced AdvancedSimulationStep01.scala AdvancedSimulationStep02.scala AdvancedSimulationStep03.scala AdvancedSimulationStep04.scala AdvancedSimulationStep05.scala

bin/目录存放gatling的可执行文件,conf/存放配置,一般保持默认便可,lib/存放gatling自己的依赖,用户不用管,results/存放报告,user-files/是用户最主要使用的目录,用户定义的测试场景相关的代码均存放于此目录下。框架

zip包解压缩之后已经带有了一个官方的示例文件BasicSimulation.scala,想看看演示效果的直接使用bin/gatling.(bat|sh)启动就能够了。这个演示的场景描述见官方文档。那几个AdvancedSimulationStep其实效果上和BasicSimulation彻底一致,只是官方提供了一些参考的DSL写法而已。dom

一些实战中的DSL参考范例

尽管gatling和jmeter同样,带有一个图形化的recorder,可是功能极其简陋,只能模拟一个用户,而且没有结构化代码架构。所以只能用来生成最基本的框架,绝大多数状况须要用户本身编写DSL,其实官方文档中几乎已经涵盖了大部分的用例,照着抄就能够了。这里提供几个参考的DSL

Random不起做用?

有时候咱们须要在测试场景中引入随机数,从而更好的模拟大量用户请求的场景。很天然的想到几乎各个编程语言都带有Random函数库。而Scala天然也不例外,带有一个scala.util.Random类库。可是实际使用的时候可能会发现没用。好比下面这个例子:

forever(
    exec(http("Random id browse") .get("/articles/" + scala.util.Random.nextInt(100)) .check(status.is(200)) )

这个scala.util.Random.nextInt(100)会发现只有第一次会随机生成一个数字,后面都不变。按照gatling的官方文档的解释,因为DSL会预编译,在整个执行过程当中是静态的。所以Random在运行过程当中就已经静态化了,不会再执行。应改成Feeder实现。Feeder是gatling用于实现注入动态参数或变量的。改用Feeder实现:

val randomIdFeeder = Iterator.continually(Map("id" -> (scala.util.Random.nextInt(100)))) forever( feed(randomIdFeeder) .exec(http("Random id browse") .get("/articles/${id}")) .check(status.is(200)) )

feed()在每次执行时都会从Iterator[Map[String, T]]对象中取出一个值,这样才能实现这个需求。

使用import引入外部方法

例如专门写一个Feeders.scala文件,存储着各类须要用到的Feeder:

import scala.util.Random object LinchangFeeders { def randomGeoFeeder() : Iterator[Map[String, Number]] = { val LNG_RANGE = List(108.75, 109.1) val LAT_RANGE = List(34.0, 34.4) return Iterator.continually( Map( "lng" -> ( Random.nextFloat() * (LNG_RANGE(1) - LNG_RANGE(0)) + LNG_RANGE(0) ) ,"lat" -> ( Random.nextFloat() * (LAT_RANGE(1) - LAT_RANGE(0)) + LAT_RANGE(0) ) ) ) } def randomOffsetFeeder() : Iterator[Map[String, Number]] = { Iterator.continually(Map("offset" -> Random.nextInt(100))) } }

而后在MySimulation.scala就能够import,使用里面定义好的方法了:

import scala.concurrent.duration._ import scala.util.Random import io.gatling.core.Predef._ import io.gatling.http.Predef._ import io.gatling.jdbc.Predef._ import Feeders._ class MySimulation extends Simulation { val brownse = feed(randomOffsetFeeder) .exec( home ) }

用户注入策略

  • <= 10: 一把注入
  • > 10: 每10秒注入10个用户
val injectStrategy = if (USERS_COUNT > 10) { splitUsers(USERS_COUNT) into( rampUsers(10) over(5 seconds) ) separatedBy(10 seconds) } else { atOnceUsers(USERS_COUNT) }

压测时间策略

  • = 0: 全部模拟用户不循环,执行完测试场景即退出
  • > 0: 全部模拟用户循环执行测试场景,直到达到指定时间
val scn = scenario("My test scenario") .doIfOrElse(DURATION > 0) { forever( exec(steps) ) } { exec(steps) } val setup = setUp( scn.inject( injectStrategy ).protocols(httpProtocol) ) if (DURATION > 0) { setup.maxDuration(DURATION minutes) }
相关文章
相关标签/搜索