1 介紹
Gatling是一款基於Scala 開發的高性能服務器性能測試工具,它主要用於對服務器進行負載等測試,並分析和測量服務器的各種性能指標。Gatling主要用於測量基於HTTP的服務器,比如Web應用程序,RESTful服務等,除此之外它擁有以下特點:
Gatling適用的場景包括:測試需求經常改變,測試腳本需要經常維護;測試環境的客戶機性能不強,但又希望發揮硬件的極限性能;能對測試腳本進行很好的版本管理,並通過CI進行持續的性能測試;希望測試結果輕量易讀等。
2 與Jmeter對比:
圖1和圖2分別展現了二者在並發性能方面的表現。
圖1,JMeter 2.8
圖2,Gatling 1.3.2
3 使用、
3.1 下載:http://gatling.io/#/download,解壓即可使用
3.2 文件目錄介紹
當運行gating腳本的時候,其會掃描user-files目錄下的所有文件,列出其中所有的Simulation(一個測試類,裡面可以包含任意多個測試場景)。選擇其中一個Simulation,然後填寫Simulation ID和運行描述,這個都是為報告描述服務的。
3.3 錄制-
3.3.1 啟動錄制:
3.3.2 界面:
3.3.3 錄制配置
需要配置Internet的代理才能錄制成功。錄制完成之後代碼保存在user-files/simulations/ 目錄下
4 運行:
4.1 運行腳本
4.2 運行的時候會把所有的能運行的場景(simulation)都列出來,選擇想要運行的即可。
5 測試結果
Gatling測試報表基於HTML,並且在測試過程中業已生成,所以打開速度很快。而且,當把鼠標移動到不同數據軸上時,都會有彈出框顯示這個點上詳細的測試數據信息。這種動態顯示數據的方式非常方便查看和分析數據。考慮到項目真實數據的不便,我將通過Gatling官方網站給出的示例報表進行說明。
Gatling的報表分為兩類:GLOBAL和DETAILS,其中GLOBAL主要是請求相關的統計數據,比如每秒請求數,請求成功與失敗數等;其中DETAILS主要是請求時間相關的統計數據,比如請求響應時間,請求響應延遲時間等。
圖4 每秒請求數,
當鼠標放到圖中任何一個點的時候,對應時間點上請求的詳細數據就會以圖中白色的彈出框的方式進行顯示。在下面的請求響應延遲時間圖裡面也有同樣的功能。
圖5 請求響應延遲時間,
6 進階
腳本解析:
package computerdatabase // 1 包名
import io.gatling.core.Predef._ // 2必須導入的
import io.gatling.http.Predef._
import scala.concurrent.duration._
class BasicSimulation extends Simulation { // 3 類聲明,必須繼承Simulation
val httpConf = http // 4 所有Http請求普遍配置
.baseURL("http://computer-database.gatling.io") // 5 base URL
.acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") // 6 請求頭
.doNotTrackHeader("1")
.acceptLanguageHeader("en-US,en;q=0.5")
.acceptEncodingHeader("gzip, deflate")
.userAgentHeader("Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0")
val scn = scenario("BasicSimulation") // 7 定義場景
.exec(http("request_1") // 8 http請求名稱 request_1,這個名稱最後會顯示在報告中
.get("/")) // 9 get請求方法
.pause(5) // 10 暫停/思考時間 5s
setUp( // 11 建立場景
scn.inject(atOnceUsers(1)) // 12 聲明注入一個用戶
).protocols(httpConf) // 13 之前聲明的Http請求配置
}
6.1 分離場景:分離操作:如浏覽、搜索、編輯操作
object Search{valsearch=exec(http("Home")// let's give proper names, as they are displayed in the reports
.get("/")).pause(7).exec(http("Search").get("/computers?f=macbook")).pause(2).exec(http("Select").get("/computers/6")).pause(3)}objectBrowse{valbrowse=???}objectEdit{valedit=???}
We can now rewrite our scenario using these reusable business processes:
valscn=scenario("Scenario Name").exec(Search.search,Browse.browse,Edit.edit)
6.2 設置常規用戶和管理員賬戶
val users=scenario("Users").exec(Search.search,Browse.browse)
val admins=scenario("Admins").exec(Search.search,Browse.browse,Edit.edit)
6.3 設置虛擬用戶
setUp(users.inject(atOnceUsers(10)).protocols(httpConf))
6.4 設置上升壓力,即每隔一段時間啟動多少用戶
setUp(users.inject(rampUsers(10)over(10seconds)),admins.inject(rampUsers(2)over(10seconds))).protocols(httpConf)
10s 內啟動10個users和2個admin
6.5 參數化 or 動態數據
6.5.1 創建文件:在user-files/data文件夾下創建cvs文件,如:search.csv ,內容如下:
searchCriterion,searchComputerName
Macbook,MacBook Pro
eee,ASUS Eee PC 1005PE
6.5.2 使用文件:
objectSearch{
valfeeder=csv("search.csv").random// 1, 2
valsearch=exec(http("Home").get("/")).pause(1).feed(feeder)// 3
.exec(http("Search").get("/computers?f=${searchCriterion}")// 4
.check(css("a:contains('${searchComputerName}')","href").saveAs("computerURL")))// 5
.pause(1).exec(http("Select").get("${computerURL}"))// 6
.pause(1)}
6.5.3 解釋:
Explanations:
6.6 循環調用
In the browse process we have a lot of repetition when iterating through the pages. We have four times the same request with a different query param value. Can we change this to not violate the DRY principle?
First we will extract the repeated exec block to a function. Indeed, Simulation‘s are plain Scala classes so we can use all the power of the language if needed:
objectBrowse{defgotoPage(page:Int)=exec(http("Page "+page).get("/computers?p="+page)).pause(1)valbrowse=exec(gotoPage(0),gotoPage(1),gotoPage(2),gotoPage(3),gotoPage(4))}
We can now call this function and pass the desired page number. But we still have repetition, it’s time to introduce another builtin structure:
objectBrowse{valbrowse=repeat(5,"n"){// 1exec(http("Page ${n}").get("/computers?p=${n}"))// 2.pause(1)}}
Explanations:
6.7 結果校驗:()
importjava.util.concurrent.ThreadLocalRandom// 1
valedit=exec(http("Form").get("/computers/new")).pause(1).exec(http("Post").post("/computers").check(status.is(session=>200+ThreadLocalRandom.current.nextInt(2))))// 2
Explanations:
1.To handle this random failure we use the tryMax and exitHereIfFailed constructs as follow:
valtryMaxEdit=tryMax(2){// 1exec(edit)}.exitHereIfFailed// 2
Explanations:
7 其他
7.1 可以與CI集成
7.2 實時監控: 支持用戶數和請求數的實時監控
7.3 服務器監控功能(System under test metrics)。
參考鏈接: