美文网首页DTeam团队日志
使用Gatling做web压力测试

使用Gatling做web压力测试

作者: 冯宇Ops | 来源:发表于2017-01-30 22:16 被阅读1296次

    Gatling是什么

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

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

    Gatling VS Jmeter

    Jmeter是目前非常成熟的负载测试工具,支持相当多的协议,支持插件,可以轻松的扩展。

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

    关于Jmeter和Gatling的一个比较好的对比可以参见infoq的文章

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

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

    Gatling的基本使用

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

    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写法而已。

    一些实战中的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)
        }
    

    错误处理

    这个是gatling的一大亮点。在压力测试的过程中,无可避免会遇到各种花式错误。比如服务器超时无响应,服务端执行错误返回了非预期结果等等。这些错误如果不进行处理,将会影响后续测试。

    比如后续所有链接请求都依赖于登录成功,一旦登录失败,后续请求将无任何意义,而且会影响到最终汇总的测试结果。

    gatling可以通过check指令检测URL的返回结果是否符合预期(如返回的HTTP code,返回的内容是否包含预期的内容等等)。通过tryMax, doIf等指令进行失败重试以及处理链接之间的依赖问题。

    更多关于失败处理可以参考: http://gatling.io/docs/2.2.3/advanced_tutorial.html#step-05-check-and-failure-management

    比如一个简单的登录请求的DSL:

      val login = tryMax(MAX_RETRY) {
        pause(PAUSE_BEFORE_RETRY)
          .exec(http("登录系统")
            .post("/login")
            .formParam("code", "${code}")
            .headers(jwtRequestHeader)
            .check(status.is(200),
              jsonPath("$.token").find.saveAs("token")))
      }
    
      val brownse = doIf("${token.exists()}") {
        exec(
          // other steps
        )
      }
    

    登录成功会返回一个JSON,包含有token属性,将token存储于session(这个session指gatling的session,作用是存储每个虚拟用户各自的属性,并不是服务器端的session),用于以后的登录请求。通过check期望返回200 OK,并且期望返回一个token属性。

    由于后续的请求都必须依赖于token属性存在,因此使用doIf来确保这个依赖关系成立,遇到登录失败时将不会继续向下请求。

    相关文章

      网友评论

        本文标题:使用Gatling做web压力测试

        本文链接:https://www.haomeiwen.com/subject/wwuhittx.html