Edited at

ガトリング砲をぶっ放した話

More than 1 year has passed since last update.


はじめに


Gatlingとは?



  • GatlingはScalaをDSLとする負荷テストツールです。

  • ab(apache-bench)より高機能で, Jmeterよりシナリオを書きやすい(らしい)のが特徴です


    • 実はJmeter, 使ったことがないです..



  • Gatlingという名前は田代砲になぞらえたのでしょうか


Gatlingのいいところ


  • シナリオがXMLではなく, Scalaで書ける


    • 普通のプログラムと同じ感覚で書くことが出来るので, シナリオの可読性を高くすることが出来る

    • また, プログラムなのでどうにでも工夫することができる

    • Scala入門としても最適(?)



  • 出力結果がキレイ


    • Xではなく, HTMLで出力される

    • X環境(X.Org)を整備しておく必要がない, Javaの実行環境のみ整っていればいい



  • 名前がカッコイイ


実行環境の準備


  • 実行に際してbuild.sbtを準備し実行する方法もありますが, bundleされているものを使用する方法が最も簡単・便利なので以下の方法をおすすめいたします。

$ sudo yum -y install java-1.8.0-openjdk wget unzip # CentOSの場合

$ wget https://repo1.maven.org/maven2/io/gatling/highcharts/gatling-charts-highcharts-bundle/2.2.3/gatling-charts-highcharts-bundle-2.2.3-bundle.zip -O gatling-2.2.3.zip
$ unzip gatling-2.2.3-bundle.zip


シナリオの書き方について


  • Google Chrome Developer Toolsの"Copy as cURL"を用いるとシナリオ作成に大変便利です。

  • 以下の例を参考にしていただけるとよいかと思います。


TestSimulation.scala

package test //複数シナリオを書いておく場合, パッケージでまとめておくとわかりやすくていいです


import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class TestSimulation extends Simulation {

// シングルトンオブジェクトを生成する
object uri {
val fqdn = "example.com"
val base = "https://" + fqdn
val api(req:String): String = {
"/api" + req
}
}

// UserAgent
object userAgent {
val iPhone = "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_1 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A403 Safari/602.1"
val android = "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; Nexus One Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
}

// リソースごとに異なるヘッダー
object headers {

val top_referer = uri.base + "/index.html"

// APIのリクエストヘッダ
// フォーム送信時の形式はx-www-form-urlencodedを指定
// curl でいうところの--dataにあたる
val api = Map(
"Accept" -> "application/json,text/javascript,*/*;q=0.01",
"Origin" -> uri.base,
"X-Requested-With" -> "XMLHttpRequest", //XHRからのリクエストであることを明示
"Content-Type" -> "application/x-www-form-urlencoded; charset=UTF-8",
"Referer" -> top_referer
)

// Javascriptのリクエストヘッダ
val js = Map(
"Content-Type" -> "application/javascript; charset=UTF-8",
"Referer" -> top_referer
)

// HTMLのリクエストヘッダ
val html = Map(
"Content-Type"-> "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
)
}

// 全リソース共通の設定
val httpConf = http.baseURL(uri.base)
.acceptLanguageHeader("en-US,en;q=0.8")
.acceptEncodingHeader("gzip,deflate,sdch,br")
.userAgentHeader(userAgent.iPhone)
.connectionHeader("keep-alive")
.header("Cookie", "nickname=Nate-River56; place=Shibuya, Tokyo")
.header("Host", uri.fqdn)
.header("Pragma", "no-cache")
.header("Cache-Control", "no-cache")
.maxRedirects(5) //リダイレクトの最大回数

object scn {

val stayRange = (0 milliseconds, 100 milliseconds)

val top = scenario("TopPage#1") //わかりやすい名前を付ける
.exec(
http("index.html")
.get(uri.static("/index.html")) //リソースを指定
.headers(headers.html) //HTML用のヘッダーを付加
.check(status.is(200)) //HTTP 200であればOK, そうでなければNGでカウントされエラーとして表示される
.check(regex("<center><h1>Hello</h1></center>").exists) //指定した要素が含まれているか
)
//リソースの間の待ち時間を"Uniform random"の形式で指定
//繰り返しシナリオを実行した際にリソースへのアクセスをバラけさせるため
.pause(stayRange._1, stayRange._2)
.exec(
html("app.js")
.get(uri.static("/scripts/app.js"))
.queryParam("tm", "20161212") //クエリストリングを付加, ?tm=20161212と同じ
.disableFollowRedirect //リダイレクトを無効に
.headers(headers.js)
.check(status.is(200))
)
.pause(stayRange._1, stayRange._2)
.exec(
http("status.json")
.post(uri.api("status.json"))
.headers(headers.api)
.formParam("key1", "value1") //パラメータを指定
.formParam("key2", "value2")
.check(status.is(200))
)
}

setUp(
scn.top.inject(
nothingFor(5 seconds), //間違えて実行したときのため, 5秒間何もしない
atOnceUsers(3), //3回だけワッとシナリオを実行する
rampUsersPerSec(1) to (30) during (120 seconds), //120秒かけてユーザー数を30まで増加, パラメータは適宜調整する
constantUserPerSec(30) during (180seconds) //180秒の間, ユーザー数30を維持する
)
).protocols(httpConf)
}



実行方法


  • user-files/simulation以下にDSLを配置し, 以下のように実行します。


    • ビルドが重く, 特に初回非常に重いので気長にお待ち下さい..



$ cd ~/gatling-charts-highcharts-bundle-2.2.3

$ ./bin/gatling.sh
GATLING_HOME is set to ~/gatling-charts-highcharts-bundle-2.2.3
14:48:23.772 [WARN ] i.g.c.ZincCompiler$ - Pruning sources from previous analysis, due to incompatible CompileSetup.
Choose a simulation number:
[0] computerdatabase.BasicSimulation
[1] computerdatabase.advanced.AdvancedSimulationStep01
[2] computerdatabase.advanced.AdvancedSimulationStep02
[3] computerdatabase.advanced.AdvancedSimulationStep03
[4] computerdatabase.advanced.AdvancedSimulationStep04
[5] computerdatabase.advanced.AdvancedSimulationStep05 //[0]-[5]はサンプルのシナリオ
[6] test.TestSimulation

Select simulation id (default is 'basicsimulation'). Accepted characters are a-z, A-Z, 0-9, - and _
6

Select run description (optional)
これはテストです

Simulation computerdatabase.BasicSimulation started...

================================================================================
2016-12-12 14:50:13 5s elapsed
---- Requests ------------------------------------------------------------------
> Global (OK=2 KO=0 )
> request_1 (OK=1 KO=0 )
> request_1 Redirect 1 (OK=1 KO=0 )

---- Scenario Name -------------------------------------------------------------
[--------------------------------------------------------------------------] 0%
waiting: 0 / active: 1 / done:0
================================================================================

--- 省略 ---

================================================================================
2016-12-12 14:50:36 28s elapsed
---- Requests ------------------------------------------------------------------
> Global (OK=13 KO=0 )
> request_1 (OK=1 KO=0 )
> request_1 Redirect 1 (OK=1 KO=0 )
> request_2 (OK=1 KO=0 )
> request_3 (OK=1 KO=0 )
> request_4 (OK=1 KO=0 )
> request_4 Redirect 1 (OK=1 KO=0 )
[> request_5 (OK=1 KO=0 )
> request_6 (OK=1 KO=0 )
> request_7 (OK=1 KO=0 )
> request_8 (OK=1 KO=0 )
> request_9 (OK=1 KO=0 )
> request_10 (OK=1 KO=0 )
> request_10 Redirect 1 (OK=1 KO=0 )

---- Scenario Name -------------------------------------------------------------
[##########################################################################]100%
waiting: 0 / active: 0 / done:1
================================================================================![スクリーンショット 2016-12-12 15.12.22(2).png](https://qiita-image-store.s3.amazonaws.com/0/61315/686adf04-442c-099b-52ca-cf8fe4715811.png)

Simulation computerdatabase.BasicSimulation completed in 26 seconds
Parsing log file(s)...
Parsing log file(s) done
Generating reports...

================================================================================
---- Global Information --------------------------------------------------------
> request count 13 (OK=13 KO=0 )
> min response time 257 (OK=257 KO=- )
> max response time 752 (OK=752 KO=- )
> mean response time 337 (OK=337 KO=- )
> std deviation 150 (OK=150 KO=- )
> response time 50th percentile 263 (OK=263 KO=- )
> response time 75th percentile 265 (OK=265 KO=- )
> response time 95th percentile 612 (OK=612 KO=- )
> response time 99th percentile 724 (OK=724 KO=- )
> mean requests/sec 0.481 (OK=0.481 KO=- )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms 13 (100%)
> 800 ms < t < 1200 ms 0 ( 0%)
> t > 1200 ms 0 ( 0%)
> failed 0 ( 0%)
================================================================================

Reports generated in 0s.
Please open the following file: ~/gatling-charts-highcharts-bundle-2.2.3/results/0-1481521808046/index.html


  • 実行結果のフォルダ(0-1481521808046/)をローカルに移動しブラウザで確認すると負荷テストの結果をみることが出来ます


負荷テストの結果


  • .check()に成功していれば, OKにカウントされそうでなければNGにカウントされます。

  • また各リソースのResponse Time Number, レスポンス時間のパーセンタイルをみることが出来ます
    Gatling_global.png



  • 全体の1秒あたりのレスポンス数の推移を確認することができ, 想定通り捌けているかを確認することができます
    Gatling_rps.png



  • 個別のリソースに関しても, Response Time Number, レスポンス時間の分布をみることができます
    Gatling_each.png


注意


  • 負荷テストを自らが管理していないサーバーに対して行い過大な負荷をかけることはDoS攻撃にあたり, 電子計算機損壊等業務妨害罪等に問われることになります。

  • Gatlingに限ったことではありませんが, 負荷テストツールをDoS攻撃に利用することは絶対にしないでください。


参考リンク


公式Document

http://gatling.io/docs/2.2.3/


UserAgentについて

http://qiita.com/nightyknite/items/b2590a69f2e0135756dc


負荷のかけ方について

http://qiita.com/noexpect/items/2f52ae16ec20a5ba14b4


Gatlingの使用例

http://shanon-tech.blogspot.jp/2015/10/scala-gatling.html


まとめ


  • JMeterのようにGatlingでmaster-slave構成の負荷テスト環境をつくることは難しいです。

  • 比較的大きいサービスに対する負荷テストでなければscalaでシナリオを書くのも楽しいですし, ぜひ一度Gatling砲をぶっ放してみてはいかがでしょうか?


最後に


  • さて, 次回は一年上の先輩であるcy-kenta-takahashiさんの"UIPageViewControllerを使って漫画ビューアの見開き表示に対応した話"です。非常に楽しみです!


    • 業務ではあまり関わることがありませんが, 社内イベント等で関わることの多い先輩です!

    • 今度スプラトゥーンでぜひ対戦したいです