Sunday, June 29, 2014

Gatling筆記: 完成自己的simulation

[基本單元]
1. scenaio: 描述使用情境, 一個simulation可以指定多個scenario
2. exec: 指定要做的事, 通常會是http request, 一個scenario裡面用exec串接多個動作
3. http: 設定request格式, 也可加入response檢查
4. httpConfig: 設定整個scenario共用的protocol(要打哪個domain, 要不要自動redirect等等)
5. users: 要用幾個user來跑
6. ramp: 設定每個user一開始起來跑的間隔
scn.users(10).ramp(10) // 10 users/10s = 1 user/s
scn.users(10).ramp(20) // 10 users/20s = 0.5 user/s = 1 user every 2s
scn.users(1000).ramp(100) // 1000 users/100s = 10 users/s

[HTTP]
1. GET: 以下是基本的HTTP GET範例
val httpConf = httpConfig.baseURL("http://my.website.tld")

val scn = scenario("My Scenario")
  .exec(
    http("My Request")
    .get("/my_path") // Will actually make a request on "http://my.website.tld/my_path"
  )
  .exec(
    http("My Other Request")
    .get("http://other.website.tld") // Will make a request on "http://other.website.tld"
  ...

setUp(scn.protocolConfig(httpConf)...)
2. 帶query string:
- 直接串在uri上
- queryParam(key: String, value: String)

3. 加header:
- 加一個: header(key: String, value: String)
- 加多個: header(Map[String, String]())

4.帶request body:
- 直接給字串: body(body: String)
- 從檔案讀: fileBody(fileName: String)
** 要注意檔案要放在galting目錄的這個路徑底下user-files/request-bodies/
- 從檔案讀且檔案內有變數可以替換: fileBody(templateFileName: String, valuesToReplace: Map[String, String])
** 同樣檔案要放在galting目錄的user-files/request-bodies/路徑底下, 且副檔名須為ssp
- 給byte array: byteArrayBody (byteArray : (Session) => Array[Byte])
- 另外還有POST特有的form以及multi-part格式

[Session]
session是一個Map, key為String, value可以是任意類型,
每個user在執行scenario時都有一個專屬的session可以暫存各種之後會用到的東西
1. 儲存:
- saveAs("myKey"), 通常是與Check合在一起用
- setAttribute("myKey", "myValue")
2. 取出:
- "${myKey}"
3. 如果對語法不熟悉或是發生什麼怪狀況需要debug時, 可以在用exec特別處理session
** 因為session是不能修改的, 所以當你動到session的內容時, 它其實是產生一個新的session物件給你
- getAttribute(key: String): Any = getTypedAttribute[Any](key)
- getTypedAttribute[X](key: String)
- getAttributeAsOption[T](key: String): Option[T]
- setAttributes(attributes: Map[String, Any])
- setAttribute(attributeKey: String, attributeValue: Any)
- removeAttribute(attributeKey: String)
- isAttributeDefined(attributeKey: String)
- getCounterValue(counterName: String)
- getTimerValue(timerName: String)
.exec(session =>
  session.setAttribute("foo", "bar")
  println(session)
  session
)

[Feeder]
用來餵參數用的好東西, 通常會是讀檔但也把JDBC或Redis當作來源,
1. 讀檔怎麼讀
- csv( filename: String ), 每個值是用,分隔
- ssv( filename: String ), 每個值是用;分隔
- tsv( filename: String ), 每個值是用TAB分隔
- 自訂分隔符號csv("user_credentials.csv", '#')
2. 每次呼叫feeder時, 會從來源取得一筆資料, 有幾種模式
- queue(預設): 照順序讀進來, 讀到底之後simulation會終止, 使用這個模式的時候要注意給的資料要夠多
- random: 隨機
- circular: 照順序讀進來, 讀到底之後會從頭讀
3. 如何存取
資料被feeder讀進來之後會被放在session裡, 如果是從檔案讀的話, key就是對應到檔案第一行的名稱
username,password
john,smith21
john,doe43
4. 使用自己的feeder
Feeder其實是一個簡單的class, 當你呼叫它的提供一個next函式時會回傳一個Map.
package object myfeeders {
  import com.excilys.ebi.gatling.core.feeder._

  val myAccountFeeder = new Feeder[String] {
    import org.joda.time.DateTime
    import scala.util.Random

    private val RNG = new Random

    // random number in between [a...b]
    private def randInt(a:Int, b:Int) = RNG.nextInt(b-a) + a

    private def daysOfMonth(year:Int, month:Int) = new DateTime(year, month, 1, 0, 0, 0, 000).dayOfMonth.getMaximumValue

    // always return true as this feeder can be polled infinitively
    override def hasNext = true

    override def next: Map[String, String] = {
      val email = scala.math.abs(java.util.UUID.randomUUID.getMostSignificantBits) + "_gatling@dontsend.com"
      val year = randInt(1945, 1994)
      val month = randInt(1, 12)
      val day = randInt(1, daysOfMonth(year, month))

      Map("contactEmail" -> email, 
        "birthdayYear" -> year.toString, 
        "birthdayMonth" -> month.toString, 
        "birthdayDay" -> day.toString)
    }
  }
}

[Check]
就是可以讓你檢查response是否與你預期相同,
檢查的範圍除了http status,還可以檢查payload格式是否正確, 甚至可以抓特定的值出來確認
1. status
- check(status.is(200))
- check(status.not(404), status.not(500)))
2. body
- check(regex("""..."""))
- check(xpath(".."))
- check(jsonPath(""))
3. 另外可以用find把你想要的值抓出來, 是需求決定是否要套上transform將值做些轉換或是用saveAs把它存在session裡

[範例]
最後的最後有一些流程控制的東西懶得寫了, 就直接看範例吧!
package collection

import com.excilys.ebi.gatling.core.Predef._
import com.excilys.ebi.gatling.http.Predef._
import com.excilys.ebi.gatling.http.Headers.Names._
import akka.util.duration._
import bootstrap._
import CLTHeaders._
import myfeeders._

class MyCollectionSimulation extends Simulation {
 val httpConf = httpConfig.baseURL("http://localhost:9000/")

  val get_chain = exec(http("get collection")
          .get("api/collection/${cid}")          
          .headers(auth_headers)
          .check(status.is(200)))

  val update_chain = exec(http("update collection")
          .post("api/")
          .fileBody("Update", Map("cid" -> "${cid}", "title" -> "${title}")).asJSON
          .headers(auth_headers)
          .check(status.is(200))) 

  val my_scenario = scenario("CRUD collection")
        .during(3 minutes) {
          feed(myAccountFeeder)
          .exec(http("create collection")
            .post("api/")
            .fileBody("CreateCollection.json").asJSON
            .headers(auth_headers)
            .check(status.is(200))
            .check(jsonPath("$.collection").saveAs("collection")))
          .exec(session=> {                   
            val tmp = session.getTypedAttribute[String]("collection").split("\"")
            val cid = tmp(1)                   
            session.removeAttribute("collection").setAttribute("cid", cid)
            })
          .repeat(2){
            get_chain      
          }
          .doIf(session=>session.getTypedAttribute[String]("cid").head.toInt>80) {
            update_chain
          }
          .exec(http("delete collection")
            .post("api/")
            .fileBody("DeleteCollection", Map("cid" -> "${cid}")).asJSON
            .headers(auth_headers)
            .check(status.is(200)))
          .exec(http("get collection")
            .get("api/collection/${cid}")          
            .headers(auth_headers)
            .check(status.is(400)))
        }

  setUp(create_scenario.users(100).ramp(10).protocolConfig(httpConf))      
}

上一集: Gatling, 威力強大卻也簡單上手的Http Server壓力測試工具
ref:
- HTTP reference
- Session
- Feeders
- Checks

No comments:

Post a Comment