Gatling簡單測試SpringBoot工程

 

 

前言

Gatling是一款基於Scala 開發的高性能服務器性能測試工具,它主要用於對服務器進行負載等測試,並分析和測量服務器的各類性能指標。目前僅支持http協議,能夠用來測試web應用程序和RESTful服務。html

除此以外它擁有如下特色:java

  • 支持Akka Actors 和 Async IO,從而能達到很高的性能web

  • 支持實時生成Html動態輕量報表,從而使報表更易閱讀和進行數據分析spring

  • 支持DSL腳本,從而使測試腳本更易開發與維護json

  • 支持錄製並生成測試腳本,從而能夠方便的生成測試腳本數組

  • 支持導入HAR(Http Archive)並生成測試腳本瀏覽器

  • 支持Maven,Eclipse,IntelliJ等,以便於開發springboot

  • 支持Jenkins,以便於進行持續集成服務器

  • 支持插件,從而能夠擴展其功能,好比能夠擴展對其餘協議的支持session

  • 開源免費

 

依賴工具

  • Maven

  • JDK

  • Intellij IDEA

 

安裝Scala插件

打開 IDEA ,點擊【IntelliJ IDEA】 -> 【Preferences】 -> 【Plugins】,搜索 「Scala」,搜索到插件而後點擊底部的 【Install JetBrains plugin…】安裝重啓便可。

 

Gatling Maven工程

建立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.batbin\gatling.sh效果一致

  • Recorder:右鍵運行跟運行 bin\recorder.batbin\recorder.sh效果一致,錄製的腳本存放在scala目錄下

  • target:存放運行後的報告

至此就可使用IntelliJ愉快的開發啦。

 

Gatling測試SpringBoot

Gatling基於Scala開發的壓測工具,咱們能夠經過錄制自動生成腳本,也能夠本身編寫腳本,你們不用擔憂,首先腳本很簡單經常使用的沒幾個,另外gatling封裝的也很好咱們不須要去專門學習Scala語法,固然若是會的話會更好。

SpringBoot測試工程示例

Maven依賴

代碼以下

<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測試腳本編寫

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

  1. 配置下head,只是簡單的請求下百度首頁,因此只定義下請求的base url,採用默認的http配置便可

    //設置請求的根路徑
      val httpConf = http.baseURL("http://localhost:8080")

     

  2. 聲明Scenario,指定咱們的請求動做

    val scn = scenario("SpringBootSimulation").during(100){
          exec(http("springboot_home").get("/helloworld"))
        }

     

    scenario裏的參數:scenario name   exec()裏的參數就是咱們的執行動做,http(「本次請求的名稱」).get(「本次http get請求的地址」)

  3. 設置併發數並組裝

     //設置線程數
      setUp(scn.inject(atOnceUsers(10)).protocols(httpConf))

    atOnceUsers:立馬啓動的用戶數,能夠理解爲併發數

這樣咱們一個簡單的腳本就完成了,能夠運行看下效果。

部分測試報告以下:

 

 

 

 

高級教程

Injection – 注入

注入方法用來定義虛擬用戶的操做

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

 

  1. nothingFor(duration):設置一段中止的時間

  2. atOnceUsers(nbUsers):當即注入必定數量的虛擬用戶

    setUp(scn.inject(atOnceUsers(50)).protocols(httpConf))

     

  3. rampUsers(nbUsers) over(duration):在指定時間內,設置必定數量逐步注入的虛擬用戶

    setUp(scn.inject(rampUsers(50) over(30 seconds)).protocols(httpConf))

     

  4. constantUsersPerSec(rate) during(duration):定義一個在每秒鐘恆定的併發用戶數,持續指定的時間

     setUp(scn.inject(constantUsersPerSec(30) during(15 seconds)).protocols(httpConf))

     

  5. constantUsersPerSec(rate) during(duration) randomized:定義一個在每秒鐘圍繞指定併發數隨機增減的併發,持續指定時間

     setUp(scn.inject(constantUsersPerSec(30) during(15 seconds) randomized).protocols(httpConf))

     

  6. rampUsersPerSec(rate1) to (rate2) during(duration):定義一個併發數區間,運行指定時間,併發增加的週期是一個規律的值

     setUp(scn.inject(rampUsersPerSec(30) to (50) during(15 seconds)).protocols(httpConf))

     

  7. rampUsersPerSec(rate1) to(rate2) during(duration) randomized:定義一個併發數區間,運行指定時間,併發增加的週期是一個隨機的值

    setUp(scn.inject(rampUsersPerSec(30) to (50) during(15 seconds) randomized).protocols(httpConf))

     

  8. 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))

     

  9. splitUsers(nbUsers) into(injectionStep) separatedBy(duration):定義一個週期,執行injectionStep裏面的注入,將nbUsers的請求平均分配

    setUp(scn.inject(splitUsers(50) into(rampUsers(10) over(10 seconds)) separatedBy(10 seconds)).protocols(httpConf))

     

  10. 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請求

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

 

Feed 動態參數

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]]對象中取出一個值,這樣才能實現動態參數的需求。

相關文章
相關標籤/搜索