Gatlingによる負荷試験を行うGradleプロジェクトを作る

Gatling

まだJMeterで消耗してるの?などと煽る気は無いのですが、Webアプリケーションの性能試験などで負荷をかけるときはJMeterを使うのが実績も多くOSSのなので費用もかからないということで主流なのでしょうけれど、最近はGatlingというのが流行りつつある?ようなのでちょっと試してみました。今回はGradleプロジェクトとして作成してみます。

前提バージョン

  • Scala:2.12.6
  • Java:1.8.0_171
  • Gradle:4.8.1
  • Gatling Plugin for Gradle:0.7.3

Gradle Pluginは公式のものではなさそうですし、バージョンも0系ですがテストツールですのでね。気にせず使って良いと思いますし何ならコントリビュート・・・できたら良いねくらいの感じで。

環境構築

Javaのインストールは割愛します。GatlingはScala製のツールなのでScalaをインストールしておきます。公式サイトはこちら。

The Scala Programming Language

私の場合はWindows用のインストーラをダウンロードして使用しました。また、併せてIntelliJのプラグインを検索してScalaSBTを入れておきました。

試験用アプリケーションの作成

今回は簡単なREST-APIを用意して試験用のアプリケーションにします。Spring Initializerを使用してKotlinのプロジェクトを作成し、WebとSecutiry(Basic認証を有効にするため)の依存関係を追加しておきます。

出来上がりはこちらに。

kawakamitor/blog-materials
Contribute to kawakamitor/blog-materials development by creating an account on GitHub.

わかりやすくということでホントにちょっとしたものですが。

Controllerの作成

こんな感じで簡単なGETメソッドを空けておきます。

@RestController
class GatlingSampleController {

    private val logger = LoggerFactory.getLogger(GatlingSampleController::class.java)

    @GetMapping("{pathVariable}")
    fun get(@PathVariable pathVariable: Int, @RequestParam requestParam: Int): GatlingSampleData {

        logger.info("pathVariable is ${pathVariable} and requestParam is ${requestParam}.")

        // ゼロ除算の場合エラー
        pathVariable % requestParam

        // スリープさせる
        val sleepTime = pathVariable * 100
        logger.info("Sleep ${sleepTime} ms.")
        Thread.sleep(sleepTime.toLong())

        return GatlingSampleData(pathVariable, requestParam)
    }

}

PathVariableRequestParamIntを受け取ります。軽くレスポンスのバリエーションを出してみるということで、RequestParamがゼロのときはゼロ除算でエラーに、それ意外の場合はPathVariableの値×100msだけSleepするようにします。

ログはなんとなくです。

Basic認証の設定

Spring InitializerでSecurityの依存関係を設定すると、spring-boot-starter-securityへの依存関係が設定され、デフォルトでは全てのパスに対してBasic認証が有効になります。

何も設定しないとユーザ名はuser、パスワードは起動時に生成されログに表示されますが、シナリオに設定したいのでapplication.propertiesにパスワードを設定しておきます。

spring.security.user.name=user
spring.security.user.password=password

Gradleプロジェクトの作成

試験用アプリケーションの次はクライアント側のプロジェクトを作成します。基本的にはgradle-gatling-pluginのREADMEに従って設定します。

lkishalmi/gradle-gatling-plugin
Gatling Plugin for Gradle. Contribute to lkishalmi/gradle-gatling-plugin development by creating an account on GitHub.

出来上がりはこちら。

kawakamitor/blog-materials
Contribute to kawakamitor/blog-materials development by creating an account on GitHub.

コレもお試しということでシンプルなものですが。

build.gradleの設定

プラグインの有効化とGatring自体への依存関係を設定します。

plugins {
    id "com.github.lkishalmi.gatling" version "0.7.3"
}

repositories {
    mavenCentral()
}

apply plugin: "com.github.lkishalmi.gatling"
apply plugin: 'scala'

group 'com.example'
version '1.0-SNAPSHOT'

dependencies {
    compile 'io.gatling.highcharts:gatling-highcharts:2.3.1'
}

プラグインの設定も色々可能ですが今回はデフォルト設定で使用します。詳細はREADMEに説明があります。

lkishalmi/gradle-gatling-plugin
Gatling Plugin for Gradle. Contribute to lkishalmi/gradle-gatling-plugin development by creating an account on GitHub.

ディレクトリのカスタマイズやシナリオの絞り込み、VMオプションの設定など諸々可能です。

feederデータの作成

GatlingではCSVやTSVでシナリオに組み込むデータ(feederと呼ぶらしいです)を読み込むことができます。今回はPathVariableRequestParamに設定するデータをCSVで作成してみます。

pathVariable,requestParam
10,0
9,1
8,2
7,3
6,4
5,5
4,6
3,7
2,8
1,9

1行目がシナリオから参照するときの項目名になっています。デフォルトでは、src/gatling/resources/dataまたはsrc/gatling/dataに配置することで自動的に認識されます。配置場所のカスタマイズもプラグインの設定で可能です。

今回は10パターン用意してランダムに選ばせる方法を使います。一番上のrequestParamがゼロのデータが選択されるとエラー、それ意外の場合はpathVariableの値×100msだけSleepします。

Simulationの作成

デフォルトではsrc/gatling/scalaまたはsrc/gatling/simulationsにScalaのコードとしてSimulation(シナリオ)を作成します。

package com.example.gatlingsample

import io.gatling.core.Predef._
import io.gatling.http.Predef._

class GatlingSampleSimulation extends Simulation {

  val httpConf = http
    .baseURL("http://localhost:8080/")
    .basicAuth("user", "password")

  val params = csv("params.csv").random

  val scn = scenario("GatlingSampleSimulation")
    .feed(params)
    .exec(http("get").get("""${pathVariable}?requestParam=${requestParam}"""))

  setUp(
    scn.inject(
      rampUsers(1000) over (60)
    ).protocols(httpConf)
  )
}

いくつかポイントに分けて説明します。

import

io.gatling.core.Predefおよびio.gatling.http.Predef配下をimportしておくと諸々コードが書きやすくなるようです。

import io.gatling.core.Predef._
import io.gatling.http.Predef._

Scalaのアンダースコアは色んな意味がある・・・らしいのですがここではワイルドカードでimportということですね。

Simulationの継承

Simulationクラスは抽象基底クラスであるio.gatling.core.scenario.Simulationを継承して作成します。setUpメソッドをオーバーライドして動作させるのがお作法。

class GatlingSampleSimulation extends Simulation {
    // 省略
}

HTTP Protocolの設定

今回の例では、ベースのURLとベーシック認証の設定のみ行っています。

val httpConf = http
  .baseURL("http://localhost:8080/")
  .basicAuth("user", "password")

もちろん他にも設定が可能です。HTTPヘッダ関連や、ベーシック認証意外の認証の設定、キャッシュの設定やプロキシ関係などなど。詳細は公式のリファレンスを。

Gatling Load and Performance testing - Open-source load and performance testing
Gatling is a powerful open-source load and performance testing tool for web applications. Download Gatling and start testing now!

シナリオの設定

Gatlingでは、ScenarioBuilderを定義しておき、setUpメソッド内で頻度や期間等を指定して実行するようです。

val params = csv("params.csv").random

val scn = scenario("GatlingSampleSimulation")
  .feed(params)
  .exec(http("get").get("""${pathVariable}?requestParam=${requestParam}"""))

前述のCSVファイルをランダムに読み込み、その値をGETリクエストのパスに埋め込んで実行する、というシナリオです。scenarioメソッドで指定しているシナリオ名や、httpメソッドで指定しているリクエスト名はレポート表示に使用されるので、適切な名称を付与しておきましょう。

当然ですが連続したリクエストの実行や、レスポンスから値を取得してリクエストに設定したり、途中で一定時間のWAITを入れたり、条件分岐を行うことも可能です。こちらも詳細は公式リファレンスを。

Gatling Load and Performance testing - Open-source load and performance testing
Gatling is a powerful open-source load and performance testing tool for web applications. Download Gatling and start testing now!

setUpメソッド

設定したHTTP Protocolを使用してシナリオをsetUpメソッドにて実行します。

setUp(
  scn.inject(
    rampUsers(1000) over (60)
  ).protocols(httpConf)

ScenarioBuilder#injectで実行頻度や実行期間を設定します。この指定の場合、1000ユーザ(1000回のシナリオ実行)を60秒間の間に線形ランプを使用して実行します、という指定です。

線形ランプ・・・大学は数学科を出たのですが数学全くわかりません・・・が、均等にリクエストを投入するってことです。

もちろん一気に投入するとか、少しつづ負荷を上げながら投入するというような事も可能です。こちらも詳細は公式リファレンスを。

Gatling Load and Performance testing - Open-source load and performance testing
Gatling is a powerful open-source load and performance testing tool for web applications. Download Gatling and start testing now!

実行してみる

というわけでいざテストを実行してみましょう。事前準備として、試験用のアプリケーションを起動しておきます。

Gradle Taskから実行

プラグインのタスクとしてgatlingRunというのが定義されていますので実行します。

gradle gatlingRun

ガシガシ動いているログが流れた後、レポート出力のメッセージが表示されて完了です。

Reports generated in 0s.
Please open the following file: C:\Dev\git\blog-materials\gatling\gatling-sample\build\reports\gatling\gatlingsamplesimulation-1534560679841\index.html

BUILD SUCCESSFUL in 1m 11s
4 actionable tasks: 4 executed

デフォルトでは、build/reports/gatling/[シナリオ名]-[実行ID]配下にレポートが出力されます。

レポートを見てみる

TOPはこんな感じになります。

設定したとおり、10%程度がエラー(KO)、70%が800ms以下、20%が800ms以上に分布しているのが見て取れます。

もちろんその他にも時間経過ごとのレスポンスの傾向や、シナリオだけでなくリクエストごとのレポートなども出力されます。こちらも詳細は公式リファレンスを。

Gatling Load and Performance testing - Open-source load and performance testing
Gatling is a powerful open-source load and performance testing tool for web applications. Download Gatling and start testing now!

まとめ

具体例としては非常に簡単なものですが、Gatlingによる負荷試験をGradleプロジェクトから行う方法を試してみました。

今回は解説しませんでしたが、レスポンスのアサーションなども割と細かく行えるようですし、シナリオもメソッドチェーンで直感的にかけるようになっているんじゃないかと思いました。

JenkinsなどのCIツールと組み合わせて、コンテナのオーケストレーションなどの環境セットアップ等々も含めて完全自動化、なんてこともそんなに難しくは無いんじゃないかと。ビルドの成果物としてHTMLのレポートが出るので、Jenkinsのコンソールからポチッとそのまま見れますしね。

Scalaを触ったことはなかったのですが、シナリオを組み立てる位であればリファレンス通りにやればそんなに難しくはなさそうです。JMeterの経験がそれほどあるわけでは無いのですが、かなりやりやすいのではないかと。

また、JMeterに比べで動作が軽いらしいです。軽いシナリオしか組んでおらず、かつ家のPCもそこそこスペックがいいので体感できているとは言い難いですが、まぁサクサク動いてましたよ(笑)

ということで、Scala製の負荷試験ツールGatlingを試してみました。お仕事で性能試験に絡む事があれば使ってみようかな。

この記事に対するコメント