使用Gatling作web壓力測試

Gatling是什麼

Gatling是一個使用Scala編寫的開源的負載測試框架,基於Akka和Netty,具備如下亮點:html

  • 高性能
  • 友好的HTML報告
  • 基於情境的記錄器(recoder),對開發友好的DSL

Gatling VS Jmeter

Jmeter是目前很是成熟的負載測試工具,支持至關多的協議,支持插件,能夠輕鬆的擴展。git

而Gatling性能上更有優點,而且使用Scala DSL代替xml作配置,相比jmeter要更靈活,並且更容易修改和維護。github

關於Jmeter和Gatling的一個比較好的對比能夠參見infoq的文章編程

同時,Gatling也對MavenGradle這樣的構建工具比較友好,易於集成到Jenkins中,輕鬆加入到CI流程中。api

TIPS: 在實際使用中建議版本化管理gatling的配置,使用maven插件gradle插件造成對應的maven/gradle工程項目管理,更容易,並且容量更小,升級gatling也會更方便,減小了不少手工的操做。session

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/是用戶最主要使用的目錄,用戶定義的測試場景相關的代碼均存放於此目錄下。dom

zip包解壓縮之後已經帶有了一個官方的示例文件BasicSimulation.scala,想看看演示效果的直接使用bin/gatling.(bat|sh)啓動就能夠了。這個演示的場景描述見官方文檔。那幾個AdvancedSimulationStep其實效果上和BasicSimulation徹底一致,只是官方提供了一些參考的DSL寫法而已。maven

一些實戰中的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)
    }
相關文章
相關標籤/搜索