読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

Gatlingを使った負荷テストをsbtタスクとして実行する

サーバーエンジニアの原 @zetta1985 です。

弊社ではモバイルゲームのバックエンド Web APIの負荷テストに Gatling というScala製の負荷テストツールを使用しています。

公式ドキュメントの Quick Start ではzipファイルをダウンロード・インストールするように案内されていますが、 そのままだとコードの共有には向かないディレクトリレイアウトになっています。

Scalaプロジェクトでよく用いられている sbtGatling公式のsbt用プラグインを使用することで、標準的なScalaプロジェクトのレイアウトでGatlingを使用することができます。

今回は、Gatlingをsbtのタスクとして実行する方法をご紹介します。 なお、あらかじめsbt(バージョン0.13.9)をインストールしておく必要があります。*1

sbt設定

sbtの設定ファイルであるbuild.sbtを以下の内容で作成します。

enablePlugins(GatlingPlugin)

scalaVersion := "2.11.8"

scalacOptions := Seq(
  "-encoding", "UTF-8", "-target:jvm-1.8", "-deprecation",
  "-feature", "-unchecked", "-language:implicitConversions", "-language:postfixOps")

libraryDependencies += "io.gatling.highcharts" % "gatling-charts-highcharts" % "2.2.2" % "test"
libraryDependencies += "io.gatling"            % "gatling-test-framework"    % "2.2.2" % "test"

ここには、Gatling公式のsbtプラグインを有効にすること、このプロジェクトの依存関係にgatlingが含まれることなどが記載されています。 build.sbtの詳細にはここでは触れません。*2

sbtプラグイン設定

build.sbtにはsbtプラグインへの依存関係は定義されていないため、以下のproject/plugins.sbtファイルによって別途sbtプラグインへの依存関係を定義する必要があります。

addSbtPlugin("io.gatling" % "gatling-sbt" % "2.2.0")

シナリオの定義

Gatlingを使用した負荷テスト用のシナリオ(Scalaソースコード)を src/test/scala ディレクトリ配下に配置します。 ここではサンプルとして、以下の内容でsrc/test/scala/BasicSimulation.scala を作成します。

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


class BasicSimulation extends Simulation {

  val httpConf = http
    .baseURL("http://127.0.0.1:8080")
    .headers(Map(
        HttpHeaderNames.ContentType    -> HttpHeaderValues.ApplicationJson,
        HttpHeaderNames.Accept         -> HttpHeaderValues.ApplicationJson,
        HttpHeaderNames.AcceptEncoding -> "gzip,deflate"
      ))

  val scn = scenario("Basic sinario")
    .exec(http("request_1")
      .get("/"))
    .pause(500 milliseconds)
    .exec(http("request_2")
      .get("/"))

  setUp(scn.inject(atOnceUsers(1)).protocols(httpConf))
}

上記は、http://127.0.0.1 に対して 一回リクエストを投げたあと 500ミリ秒待ってから、再度リクエストを投げる、というシナリオになっています。*3

詳細は割愛しますが、Web APIのテストということで、HTTPリクエストヘッダーのContentTypeやAcceptヘッダーにapplication/jsonを指定してみました。 実際に使用するAPIに対するシナリオは、認証や独自HTTPリクエストヘッダー、レスポンスボディのJSONのパースなどが含まれるため、もっと複雑です。

それぞれどのようにDSLを書くかについては、また次回以降に取り上げたいと思います。

負荷テストの実行

プロジェクトのレイアウトは以下のようになりました。

~/w/b/gatling_sample ❯❯❯ tree
.
├── build.sbt
├── project
│   └── plugins.sbt
└── src
    └── test
        └── scala
            └── BasicSimulation.scala

4 directories, 3 files

ここで sbt gatling:test コマンド *4 を実行すれば http://127.0.0.1:8080 に対して負荷テストが実行できます。

ローカルホストでHTTPサーバーが起動していないと無残にも失敗するので、HTTPサーバーを起動しておきましょう。 Pythonには簡単にHTTPサーバーを起動できるモジュールがデフォルトで含まれているので、それを用います。

~/w/b/gatling_sample ❯❯❯ python -m SimpleHTTPServer 8080
Serving HTTP on 0.0.0.0 port 8080 ...

HTTPサーバーが起動できたので、Gatlingの負荷テストを実行してみましょう。 初回は依存ライブラリのインストールやScalaソースコードのコンパイルなどが走るため、少し重いです。

~/w/b/gatling_sample ❯❯❯ sbt gatling:test

(略)

================================================================================
2016-08-09 18:23:27                                           1s elapsed
---- Basic sinario -------------------------------------------------------------
[##########################################################################]100%
          waiting: 0      / active: 0      / done:1
---- Requests ------------------------------------------------------------------
> Global                                                   (OK=2      KO=0     )
> request_1                                                (OK=1      KO=0     )
> request_2                                                (OK=1      KO=0     )
================================================================================

(略)

================================================================================
---- Global Information --------------------------------------------------------
> request count                                          2 (OK=2      KO=0     )
> min response time                                      4 (OK=4      KO=-     )
> max response time                                      9 (OK=9      KO=-     )
> mean response time                                     6 (OK=6      KO=-     )
> std deviation                                          2 (OK=2      KO=-     )
> response time 50th percentile                          6 (OK=6      KO=-     )
> response time 75th percentile                          7 (OK=7      KO=-     )
> response time 95th percentile                          8 (OK=8      KO=-     )
> response time 99th percentile                          8 (OK=8      KO=-     )
> mean requests/sec                                      2 (OK=2      KO=-     )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms                                             2 (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: /Users/zetta1985/work/blog/gatling_sample/target/gatling/basicsimulation-1470734605372/index.html
[info] Simulation BasicSimulation successful.
[info] Simulation(s) execution ended.
[success] Total time: 4 s, completed 2016/08/09 18:23:27

負荷テストが起動し、全てのリクエストが成功しました。胸熱ですね、わかります。

まとめ

今回は、sbtを使用したGatlingの実行方法についてご紹介しました。

今回取り上げたトピックの他に、src/test/resources にGatlingやログの設定ファイルを配置できたり、他の3rdパーティのライブラリのインストールがsbtによって楽になったりと、sbtを使用することのメリットはまだまだ他にもあります。

実践的なDSLの記述方法などについてもこのブログで解説していきたいと思っているので、お楽しみにー

*1:http://www.scala-sbt.org/download.html

*2:build.sbtに詳細に触れるとそれだけで別の記事ができてしまいます。build.sbtは非常に柔軟なので、新規で作成する際は他のプロジェクトを参考にした方がyak率を抑えられます

*3: atOnceUsers(1) としているため、シナリオ自体が1回しか実行されません。負荷テストとしては不十分ですが、サンプルということで

*4:gatling-sbtプラグインがバージョン2.1.7のときは sbt test で実行できていましたが、2.2.0になって実行方法が変わったようです