使用Gatling做web壓力測試遇到的一些情況


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) }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM