Gatling是一款基於Scala 開發的高性能服務器性能測試工具,它主要用於對服務器進行負載等測試,並分析和測量服務器的各類性能指標。目前僅支持http協議,能夠用來測試web應用程序和RESTful服務。html
除此以外它擁有如下特色:java
web
支持實時生成Html動態輕量報表,從而使報表更易閱讀和進行數據分析spring
支持DSL腳本,從而使測試腳本更易開發與維護json
支持錄製並生成測試腳本,從而能夠方便的生成測試腳本數組
支持導入HAR(Http Archive)並生成測試腳本瀏覽器
支持Maven,Eclipse,IntelliJ等,以便於開發springboot
支持Jenkins,以便於進行持續集成服務器
支持插件,從而能夠擴展其功能,好比能夠擴展對其餘協議的支持session
開源免費
Maven
JDK
Intellij IDEA
打開 IDEA ,點擊【IntelliJ IDEA】 -> 【Preferences】 -> 【Plugins】,搜索 「Scala」,搜索到插件而後點擊底部的 【Install JetBrains plugin…】安裝重啓便可。
建立Gatling提供的gatling-highcharts-maven-archetype,
在 IntelliJ中選擇 New Project
-> Maven
-> Create form archetype
-> Add Archetype
,在彈出框中輸入一下內容:
GroupId: io.gatling.highcharts ArtifactId: gatling-highcharts-maven-archetype Version: 3.0.0-RC3
點擊查看最新版本: 最新版本
以後輸入你項目的GroupId(包名)和ArtifactId(項目名)來完成項目建立,
項目建立完成後,Maven會自動配置項目結構。
注:在建立的工程,修改pom.xml文件,添加以下配置,加快構建速度:
<repositories> <repository> <id>public</id> <name>aliyun nexus</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <releases> <enabled>true</enabled> </releases> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>public</id> <name>aliyun nexus</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories>
工程項目結構以下圖:
項目目錄說明:
bodies:用來存放請求的body數據
data:存放須要輸入的數據
scala:存放Simulation腳本
Engine:右鍵運行跟運行 bin\gatling.bat
和bin\gatling.sh
效果一致
Recorder:右鍵運行跟運行 bin\recorder.bat
和bin\recorder.sh
效果一致,錄製的腳本存放在scala目錄下
target:存放運行後的報告
至此就可使用IntelliJ愉快的開發啦。
Gatling基於Scala開發的壓測工具,咱們能夠經過錄制自動生成腳本,也能夠本身編寫腳本,你們不用擔憂,首先腳本很簡單經常使用的沒幾個,另外gatling封裝的也很好咱們不須要去專門學習Scala語法,固然若是會的話會更好。
代碼以下
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
代碼以下:
@RestController public class HelloWorldController { @RequestMapping("/helloworld") public String sayHelloWorld(){ return "hello World !"; } }
Gatling基於Scala開發的壓測工具,咱們能夠經過錄制自動生成腳本,也能夠本身編寫腳本,你們不用擔憂,首先腳本很簡單經常使用的沒幾個,另外gatling封裝的也很好咱們不須要去專門學習Scala語法,固然若是會的話會更好。
import io.gatling.core.Predef._ import io.gatling.http.Predef._ class SpringBootSimulation extends Simulation{ //設置請求的根路徑 val httpConf = http.baseUrl("http://localhost:8080") /* 運行100秒 during 默認單位秒,若是要用微秒 during(100 millisecond) */ val scn = scenario("SpringBootSimulation").during(100){ exec(http("springboot_home").get("/helloworld")) } //設置線程數 // setUp(scn.inject(rampUsers(500) over(10 seconds)).protocols(httpConf)) setUp(scn.inject(atOnceUsers(10)).protocols(httpConf)) }
Gatling腳本的編寫主要包含下面三個步驟
http head配置
Scenario 執行細節
setUp 組裝
咱們以百度爲例,進行第一個GET請求測試腳本的編寫,類必須繼承 Simulation
配置下head,只是簡單的請求下百度首頁,因此只定義下請求的base url,採用默認的http配置便可
//設置請求的根路徑 val httpConf = http.baseURL("http://localhost:8080")
聲明Scenario,指定咱們的請求動做
val scn = scenario("SpringBootSimulation").during(100){ exec(http("springboot_home").get("/helloworld")) }
scenario裏的參數:scenario name exec()裏的參數就是咱們的執行動做,http(「本次請求的名稱」).get(「本次http get請求的地址」)
設置併發數並組裝
//設置線程數 setUp(scn.inject(atOnceUsers(10)).protocols(httpConf))
atOnceUsers:立馬啓動的用戶數,能夠理解爲併發數
這樣咱們一個簡單的腳本就完成了,能夠運行看下效果。
部分測試報告以下:
注入方法用來定義虛擬用戶的操做
setUp( scn.inject( nothingFor(4 seconds), // 1 atOnceUsers(10), // 2 rampUsers(10) over(5 seconds), // 3 constantUsersPerSec(20) during(15 seconds), // 4 constantUsersPerSec(20) during(15 seconds) randomized, // 5 rampUsersPerSec(10) to 20 during(10 minutes), // 6 rampUsersPerSec(10) to 20 during(10 minutes) randomized, // 7 splitUsers(1000) into(rampUsers(10) over(10 seconds)) separatedBy(10 seconds), // 8 splitUsers(1000) into(rampUsers(10) over(10 seconds)) separatedBy atOnceUsers(30), // 9 heavisideUsers(1000) over(20 seconds) // 10 ).protocols(httpConf) )
nothingFor(duration):設置一段中止的時間
atOnceUsers(nbUsers):當即注入必定數量的虛擬用戶
setUp(scn.inject(atOnceUsers(50)).protocols(httpConf))
rampUsers(nbUsers) over(duration):在指定時間內,設置必定數量逐步注入的虛擬用戶
setUp(scn.inject(rampUsers(50) over(30 seconds)).protocols(httpConf))
constantUsersPerSec(rate) during(duration):定義一個在每秒鐘恆定的併發用戶數,持續指定的時間
setUp(scn.inject(constantUsersPerSec(30) during(15 seconds)).protocols(httpConf))
constantUsersPerSec(rate) during(duration) randomized:定義一個在每秒鐘圍繞指定併發數隨機增減的併發,持續指定時間
setUp(scn.inject(constantUsersPerSec(30) during(15 seconds) randomized).protocols(httpConf))
rampUsersPerSec(rate1) to (rate2) during(duration):定義一個併發數區間,運行指定時間,併發增加的週期是一個規律的值
setUp(scn.inject(rampUsersPerSec(30) to (50) during(15 seconds)).protocols(httpConf))
rampUsersPerSec(rate1) to(rate2) during(duration) randomized:定義一個併發數區間,運行指定時間,併發增加的週期是一個隨機的值
setUp(scn.inject(rampUsersPerSec(30) to (50) during(15 seconds) randomized).protocols(httpConf))
heavisideUsers(nbUsers) over(duration):定義一個持續的併發,圍繞和海維賽德函數平滑逼近的增加量,持續指定時間(譯者解釋下海維賽德函數,H(x)當x>0時返回1,x<0時返回0,x=0時返回0.5。實際操做時,併發數是一個成平滑拋物線形的曲線)
setUp(scn.inject(heavisideUsers(50) over(15 seconds)).protocols(httpConf))
splitUsers(nbUsers) into(injectionStep) separatedBy(duration):定義一個週期,執行injectionStep裏面的注入,將nbUsers的請求平均分配
setUp(scn.inject(splitUsers(50) into(rampUsers(10) over(10 seconds)) separatedBy(10 seconds)).protocols(httpConf))
splitUsers(nbUsers) into(injectionStep1) separatedBy(injectionStep2):使用injectionStep2的注入做爲週期,分隔injectionStep1的注入,直到用戶數達到nbUsers
setUp(scn.inject(splitUsers(100) into(rampUsers(10) over(10 seconds)) separatedBy atOnceUsers(30)).protocols(httpConf))
val scn = scenario("BaiduSimulation").
exec(http("baidu_home").get("/"))
上面的測試代碼運行時只能跑一次,爲了測試效果,咱們須要讓它持續運行必定次數或者一段時間,可使用下面兩個方式:
repeat
repeat(times,counterName)
times:循環次數
counterName:計數器名稱,可選參數,能夠用來噹噹前循環下標值使用,從0開始
val scn = scenario("BaiduSimulation").repeat(100){ exec(http("baidu_home").get("/")) }
during
during(duration, counterName, exitASAP)
duration:時長,默認單位秒,能夠加單位milliseconds,表示毫秒
counterName:計數器名稱,可選。不多使用
exitASAP:默認爲true,簡單的能夠認爲當這個爲false的時候循環直接跳出,可在
循環中進行控制是否繼續
/* 運行100秒 during 默認單位秒,若是要用微秒 during(100 millisecond) */ val scn = scenario("BaiduSimulation").during(100){ exec(http("baidu_home").get("/")) }
post參數提交方式:
JSON方式
import io.gatling.core.Predef._ import io.gatling.core.scenario.Simulation import io.gatling.http.Predef._ class JsonSimulation extends Simulation { val httpConf = http.baseURL("http://127.0.0.1:7001/tst") //注意這裏,設置提交內容type val headers_json = Map("Content-Type" -> "application/json") val scn = scenario("json scenario") .exec(http("test_json") //http 請求name .post("/order/get") //post url .headers(headers_json) //設置body數據格式 //將json參數用StringBody包起,並做爲參數傳遞給function body() .body(StringBody("{\"orderNo\":201519828113}"))) setUp(scn.inject(atOnceUsers(10))).protocols(httpConf) }
Form方式
import io.gatling.core.Predef._ import io.gatling.http.Predef._ class FormSimulation extends Simulation { val httpConf = http .baseURL("http://computer-database.gatling.io") //注意這裏,設置提交內容type val contentType = Map("Content-Type" -> "application/x-www-form-urlencoded") //聲明scenario val scn = scenario("form Scenario") .exec(http("form_test") //http 請求name .post("/computers") //post地址, 真正發起的地址會拼上上面的baseUrl http://computer-database.gatling.io/computers .headers(contentType) .formParam("name", "Beautiful Computer") //form 表單的property name = name, value=Beautiful Computer .formParam("introduced", "2012-05-30") .formParam("discontinued", "") .formParam("company", "37")) setUp(scn.inject(atOnceUsers(1)).protocols(httpConf))
RawFileBody
import io.gatling.core.Predef._ import io.gatling.core.scenario.Simulation import io.gatling.http.Predef._ class JsonSimulation extends Simulation { val httpConf = http.baseURL("http://127.0.0.1:7001/tst") //注意這裏,設置提交內容type val headers_json = Map("Content-Type" -> "application/json") val scn = scenario("json scenario") .exec(http("test_json") //http 請求name .post("/order/get") //post url .headers(headers_json) //設置body數據格式 //將json參數用StringBody包起,並做爲參數傳遞給function body() .body(RawFileBody("request.txt")) setUp(scn.inject(atOnceUsers(10))).protocols(httpConf) }
txt的文件內容爲JSON數據,存放目錄/resources/bodies
下
Gatling對參數的處理稱爲Feeder[供料器],支持主要有:
數組
val feeder = Array( Map("foo" -> "foo1", "bar" -> "bar1"), Map("foo" -> "foo2", "bar" -> "bar2"), Map("foo" -> "foo3", "bar" -> "bar3"))
CSV文件
val csvFeeder = csv("foo.csv")//文件路徑在 %Gatling_Home%/user-files/data/
JSON文件
val jsonFileFeeder = jsonFile("foo.json") //json的形式: [ { "id":19434, "foo":1 }, { "id":19435, "foo":2 } ]
JDBC數據
jdbcFeeder("databaseUrl", "username", "password", "SELECT * FROM users")
Redis
可參看官方文檔http://gatling.io/docs/2.1.7/session/feeder.html#feeder
使用示例:
import io.gatling.core.Predef._ import io.gatling.core.scenario.Simulation import io.gatling.http.Predef._ import scala.concurrent.duration._ /** * region請求接口測試 */ class DynamicTest extends Simulation { val httpConf = http.baseURL("http://127.0.0.1:7001/test") //地區 feeder val regionFeeder = csv("region.csv").random //數組形式 val mapTypeFeeder = Array( Map("type" -> ""), Map("type" -> "id_to_name"), Map("type" -> "name_to_id")).random //設置請求地址 val regionRequest = exec(http("region_map").get("/region/map/get")) //加載mapType feeder .feed(mapTypeFeeder) //執行請求, feeder裏key=type, 在下面能夠直接使用${type} .exec(http("province_map").get("/region/provinces?mType=${type}")) //加載地區 feeder .feed(regionFeeder) //region.csv裏title含有provinceId和cityId,因此請求中直接引用${cityId}/${provinceId} .exec(http("county_map").get("/region/countties/map?mType=${type}&cityId=${cityId}&provinceId=${provinceId}")) //聲明scenario name=dynamic_test val scn = scenario("dynamic_test") .exec(during(180){ regionRequest }) //在2秒內平滑啓動150個線程(具體多少秒啓動多少線程你們本身評估哈,我這裏瞎寫的) setUp(scn.inject(rampUsers(150) over (2 seconds)).protocols(httpConf)) }
注意:經過下面的代碼只會第一次調用生成一個隨機數,後面調用不變
exec(http("Random id browse") .get("/articles/" + scala.util.Random.nextInt(100)) .check(status.is(200))
Gatling的官方文檔解釋是,因爲DSL會預編譯,在整個執行過程當中是靜態的。所以Random在運行過程當中就已經靜態化了,不會再執行。應改成Feeder實現,Feeder是gatling用於實現注入動態參數或變量的,改用Feeder實現:
val randomIdFeeder = Iterator.continually(Map("id" -> (scala.util.Random.nextInt(100)))) feed(randomIdFeeder) .exec(http("Random id browse") .get("/articles/${id}")) .check(status.is(200))
feed()
在每次執行時都會從Iterator[Map[String, T]]