Gatlingは、負荷試験のシナリオをScala・Java・Kotlin・JavaScriptのコードで書き、HTMLのレポートまで自動で出すオープンソースの負荷試験ツールです。2026年10月時点の本体は3.16系で、シナリオをGitで管理し、CIの中で合否を判定する使い方に向いています。
この記事では、3.16系のsbtプロジェクトでデモを動かす手順、一覧・検索・登録を到着率で流すScalaのシミュレーション、injectの選び方、レポートの既定の指標の読み方、assertionsでビルドを止める設定までを順に扱います。最後に、Gatlingを選ぶべき開発体制と、別のツールにすべき場面を言い切ります。
まとめ:Gatlingで負荷試験を始める前に決める書く言語・注入モデル・合否条件
Gatlingで迷うのは、ツールの機能よりも最初の3つの決めごとです。上から順に決めておけば、書いたシナリオを後から作り直さずに済みます。
- 書く言語:チームがJVMの言語を書くならScalaかJava、フロントエンド中心ならJavaScriptかTypeScriptのSDKを選ぶ。DSLの語彙はどれもほぼ同じ
- 注入モデル:「毎秒何人が来るか」を確かめるなら到着率で指定するオープンモデル。Gatlingの到着率の単位はリクエストではなく、シナリオを始めるユーザーの人数
- 合否条件:assertionsでp95の応答時間とエラー率を書く。Mavenプラグインは既定でassertionsの失敗をビルドの失敗として返すため、CIでそのまま性能の回帰を止められる
レポートの「800ミリ秒未満」「1200ミリ秒以上」という区切りは既定値で、自社の目標とは無関係です。目標に合わせて設定ファイルで書き換えてから読んでください。
Gatlingの位置づけ Scala・Java・Kotlin・JSで書く負荷試験ツール
Gatlingは、ブラウザやスマートフォンアプリの代わりにHTTPリクエストを大量に送り、サーバー側の応答時間とエラーを測るツールです。画面を操作して試験を組み立てるJMeterと違い、シナリオはソースコードとして書きます。
シミュレーション・シナリオ・injectの3層で組むGatlingの基本構造
Gatlingの試験は、次の3つの部品を組み合わせて書きます。名前がそのままDSLのメソッド名になっているので、最初に覚えておくとコードが読みやすくなります。
| 部品 | 役割 | コード上の書き方 |
|---|---|---|
| シミュレーション | 1回の試験の全体 | Simulationを継承したクラス |
| シナリオ | 利用者1人の一連の操作 | scenario関数とexecの連鎖 |
| 注入プロファイル | 利用者をいつ何人入れるか | inject関数(Javaは2種類) |
| プロトコル | 接続先と共通ヘッダ | http関数とbaseUrl |
| アサーション | 合否の条件 | setUpに付けるassertions |
仮想ユーザーはスレッドを1本ずつ占有しない作りのため、1台の負荷生成マシンから数千人規模の同時利用を再現しやすい設計です。一方で、複数の負荷生成マシンへ自動で振り分けて結果を1つにまとめる機能は、有償のGatling Enterprise Editionの側にあります(InjectionのドキュメントにあるnoShardは、Enterprise Editionの負荷分散を無効にする指定として説明されています)。
3.16系で選べる言語とビルドツールの組み合わせとJDKの対応範囲
本体の最新は2026年9月29日公開のv3.16.0で、JavaScriptとTypeScript向けのSDKは2026年10月27日公開のv3.16.3が出ています。公式のデモ集se-ecommerce-demo-gatling-testsには、Javaがgradleとmaven、Scalaがgradle・maven・sbt、Kotlinがgradleとmaven、ほかにJavaScriptとTypeScriptのプロジェクトが置かれています。
JVM版を動かすJDKは、公式の最初のシミュレーションの作り方によると64ビットのOpenJDKのLTS版で、対応はJava 11から25、推奨は17か21です。2016〜2018年ごろの日本語の解説はJDK 8とzipのバンドル版を前提にしているので、手順をそのまま追うと現行版では動きません。
sbtでGatling 3.16のScala版デモを取得して最初の試験を流す手順
Scalaで書く場合は、公式デモのsbt版から始めるのが近道です。ビルドの設定とファイルの置き場所が、そのまま実案件の雛形になります。
JDK 17とsbt 1系を入れてデモを取得し1本目のシミュレーションを実行
JDKとsbtが入っていれば、次の4行で公式のデモが動きます。試験先はGatlingが用意しているデモ用のAPIで、自社のサーバーには負荷がかかりません。
java -version # 17 か 21 を推奨
git clone https://github.com/gatling/se-ecommerce-demo-gatling-tests.git
cd se-ecommerce-demo-gatling-tests/scala/sbt
sbt "Gatling/testOnly example.BasicSimulation"
実行するシミュレーションを1本に絞るのがGatling/testOnlyで、全部まとめて流すならsbt 1系ではGatling/test、sbt 2系ではGatling/testFullです(sbtプラグインのドキュメント)。終了するとコンソールに集計が出て、HTMLのレポートがtarget/gatlingの下に実行ごとのディレクトリで作られます。
build.sbtの依存関係とsrc/test配下に置くファイルの役割の確認
デモのbuild.sbtは数行しかありません。2026年11月時点の内容は、Scala 2.13.18、Gatling 3.16.0、プラグインのgatling-sbt 4.19.2です。
// build.sbt(公式デモ scala/sbt より)
enablePlugins(GatlingPlugin)
scalaVersion := "2.13.18"
val gatlingVersion = "3.16.0"
libraryDependencies += "io.gatling.highcharts" % "gatling-charts-highcharts" % gatlingVersion % "test,it"
libraryDependencies += "io.gatling" % "gatling-test-framework" % gatlingVersion % "test,it"
// project/plugins.sbt
addSbtPlugin("io.gatling" % "gatling-sbt" % "4.19.2")
シミュレーションはsrc/test/scala、テストデータのCSVやリクエスト本文のJSON、設定ファイルのgatling.confはsrc/test/resourcesに置きます。Configurationのドキュメントのとおり、gatling.confはファイルシステムではなくクラスパスから読まれるため、別の場所に置くと設定が効きません。
Scala DSLで一覧・検索・登録のシナリオを書きチェックと値の受け渡しを組む
デモが動いたら、自社のAPIに向けたシミュレーションを書きます。どの画面をどの比率で流すかは、兄弟記事の負荷試験のシナリオ設計と合否指標をまとめた記事で決めた値を使う前提です。ここではGatlingでの書き方に絞ります。
一覧と検索と登録を2つのシナリオに分けて到着率で流すコードの実例
閲覧する利用者が毎秒45人、登録する利用者が毎秒5人来る想定で、5分かけて増やしてから20分維持するシミュレーションです。45人と5人は説明用の仮定値で、実際はアクセスログから拾った値に置き換えます。
package example
import scala.concurrent.duration._
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class ItemsSimulation extends Simulation {
// 試験先は実行時に -DbaseUrl=... で渡す
val baseUrl = System.getProperty("baseUrl", "http://localhost:8080")
val httpProtocol = http
.baseUrl(baseUrl)
.acceptHeader("application/json")
.contentTypeHeader("application/json")
// 検索語を src/test/resources/keywords.csv から順に取り出す(1列目の見出しは keyword)
val keywords = csv("keywords.csv").circular
val browse = scenario("閲覧")
.exec(http("一覧").get("/api/items?page=1").check(status.is(200)))
.pause(2)
.feed(keywords)
.exec(
http("検索").get("/api/items")
.queryParam("q", "#{keyword}")
.check(status.is(200), jsonPath("$.items[0].id").saveAs("itemId"))
)
.exec(http("詳細").get("/api/items/#{itemId}").check(status.is(200)))
val register = scenario("登録")
.exec(
http("登録").post("/api/items")
.body(StringBody("""{"name":"item-#{randomUuid()}"}"""))
.check(status.is(201))
)
setUp(
browse.inject(
rampUsersPerSec(0).to(45).during(5.minutes),
constantUsersPerSec(45).during(20.minutes)
),
register.inject(
rampUsersPerSec(0).to(5).during(5.minutes),
constantUsersPerSec(5).during(20.minutes)
)
).protocols(httpProtocol)
.assertions(
global.failedRequests.percent.lt(1),
details("検索").responseTime.percentile(95).lt(800),
forAll.responseTime.percentile(95).lt(1500)
)
}
実行はsbt -DbaseUrl=https://stg.example.com "Gatling/testOnly example.ItemsSimulation"です。http("検索")の文字列はレポートとassertionsで使うリクエスト名になるので、画面や処理の名前で付けておくと結果を読むときに迷いません。
checkとsaveAsで応答を検証してフィーダーの値を次のリクエストへ渡す
このコードで押さえる書き方は3つです。check(status.is(200))は応答の検証で、条件を外れたリクエストはレポートで失敗(KO)として数えられます。jsonPath(...).saveAs("itemId")は応答から値を取り出してユーザーごとのセッションに保存し、次のリクエストで#{itemId}として使います。
#{...}はGatlingのExpression Languageで、randomUuid()やcurrentTimeMillis()などの組み込み関数も同じ書式で呼べます。検索語のような入力値はCSVからフィーダーで流し込み、.circularなら末尾まで使い切ったら先頭に戻ります。同じ検索語ばかりを投げるとキャッシュに当たり続け、応答時間が実際より短く出るので、本番の検索ログから語を数百件拾っておくと安心です。
到着率で負荷をかけるinjectの選び方とオープンとクローズドの使い分け
Gatlingの負荷の大きさは、setUpに渡す注入プロファイルで決まります。ここを読み違えると、狙った負荷の数倍や数分の1をかけてしまいます。
constantUsersPerSecの単位はリクエストでなくユーザーである点の換算
Injectionのドキュメントは、毎秒の到着人数で指定するオープンモデルの部品としてconstantUsersPerSec、rampUsersPerSec、stressPeakUsers、段階的に増やすincrementUsersPerSecなどを挙げています。注意したいのは、数えているのが「シナリオを始めるユーザー」だという点です。
上の例の閲覧シナリオは1人が一覧・検索・詳細の3リクエストを送るので、毎秒45人なら毎秒135リクエスト、登録の5件と合わせて約140リクエストになります。k6の到着率のexecutorは1回の反復を数える仕組みなので、k6のスクリプトから移植するときは「1人が何リクエスト送るか」を掛けて揃えてください。シナリオ内のpause(2)は、利用者が一覧を読んでから検索するまでの間を表します。到着率はユーザーの開始を数えるため、pauseを入れても毎秒の到着人数は変わりません。
同時ユーザー数を固定するクローズドモデルを選ぶ場面と混在の制約
同時にログインしている人数の上限や、コネクションプールの枯渇を確かめたいなら、同時実行の人数で指定するクローズドモデルを使います。ScalaではconstantConcurrentUsers(100).during(10.minutes)やrampConcurrentUsers(10).to(200).during(10.minutes)の形で、Java版ではinjectOpenとinjectClosedでメソッド自体が分かれています。
1つのシナリオのinjectに並べる部品は、すべて同じモデルでなければなりません。オープンの部品とクローズドの部品を1つのinjectに混ぜることはできないので、両方を測りたいときはシナリオかシミュレーションを分けます。システムが遅くなると到着が減ってしまうクローズドモデルの性質は、兄弟記事の負荷試験の記事にある到着率の章で詳しく扱っています。「毎秒何件まで捌けるか」が問いなら、Gatlingでもオープンモデルが基本です。
Gatlingのレポートの読み方とassertionsでCIの合否を自動判定する設定
試験が終わると、target/gatlingの下のindex.htmlにレポートができます。グラフは多いものの、合否の判断に使う箇所は限られます。
800ミリ秒と1200ミリ秒の応答時間帯とパーセンタイル既定値の書き換え
レポート冒頭の「Response time ranges」は、リクエストを応答時間で800ミリ秒未満・800〜1200ミリ秒・1200ミリ秒以上・失敗の4つに分けた図です。この境目はv3.16.0のgatling-defaults.confにあるcharting.indicatorsのlowerBound = 800とhigherBound = 1200で、表に並ぶパーセンタイルも同じ箇所の既定値で50・75・95・99です。
# src/test/resources/gatling.conf(目標に合わせて上書きする例)
gatling {
charting {
indicators {
lowerBound = 300 # 一覧の目標 p95 に合わせる
higherBound = 800 # 検索の目標 p95 に合わせる
percentile3 = 95
percentile4 = 99
}
}
}
既定のままだと、目標が300ミリ秒のAPIでも「800ミリ秒未満」が緑で表示され、合格しているように見えます。合否の基準を非機能要件の性能目標を数値に落とした記事の手順で決めたら、その値をここに写してからグラフを読んでください。
Response Time against Global RPSで見る応答時間の増加点
Static HTML reportsのドキュメントによると、リクエストごとの詳細画面にだけ「Response Time against Global RPS」という図があります。全体の毎秒リクエスト数を横軸に、そのリクエストの応答時間を並べた図で、到着率を段階的に上げた試験ではここに応答時間が急に伸び始める点が出ます。図から読み取った、応答時間が伸び始める直前の負荷が、今の構成で目標を守れる上限です。
読み違えやすいのが、時系列のパーセンタイルの図が成功したリクエストだけで計算される点です。失敗が増えた時間帯は応答時間がかえって良く見えるので、「Responses per second over time」の失敗の線と必ず並べて見ます。上限の負荷で何が先に張りついたかはUSEメソッドで資源のボトルネックを探す手順で確かめ、測った上限はキャパシティプランニングで必要台数を見積もる記事の計算の分母に戻します。
assertionsでp95とエラー率を判定しMavenのビルドを失敗させる仕組み
Assertionsのドキュメントのとおり、assertionsは試験の終了後にまとめて評価され、1つでも外れればシミュレーションは失敗です。対象はglobal(全リクエストの合計)、forAll(リクエストごと)、details("検索")(名前を指定)の3つから選びます。上の例では、全体のエラー率1%未満、検索のp95が800ミリ秒未満、すべてのリクエストのp95が1500ミリ秒未満を条件にしました。
Mavenで動かす場合、プラグインのGatlingMojoのソースではgatling.failOnErrorの既定値がtrueで、assertionsの失敗はビルドの失敗として返ります。CIの中で./mvnw gatling:test -Dgatling.simulationClass=example.ItemsSimulationを流せば、性能を落とした変更が入った時点でパイプラインが止まります。シミュレーションが複数あるのに-Dgatling.simulationClassも-Dgatling.runMultipleSimulations=trueも付けないと、非対話の実行では選べずにエラーになる点も覚えておいてください(Mavenプラグインのドキュメント)。
Gatlingを採用すべき開発体制と別のツールを選ぶべき場面の判断基準
Gatlingはどの案件にも合う道具ではありません。書く人の言語と、試験を誰がいつ回すかで向き不向きがはっきり分かれます。
JVMの言語を書くチームがCIで性能の回帰を止めたい案件はGatlingを選ぶ
Gatlingを選ぶのは、次のどちらかに当てはまるときです。
- アプリ本体がJavaやKotlin、Scalaで書かれている:負荷試験のコードを同じリポジトリ、同じビルドツール、同じIDEで保守でき、開発者が試験のコードを引き取れる
- 試験を一度きりで終わらせずCIで回し続けたい:assertionsの失敗がそのままビルドの失敗になるため、夜間に平均負荷の短縮版を流すだけで性能の劣化に気づける
開発チームに試験の保守まで渡したい場合、シナリオの初版づくりとCIへの組み込みを外部に任せ、その後の運用を社内に移す進め方が現実的です。一創の保守運用・内製化支援では、既存システムの運用を続けながら、試験のコードと手順を社内のチームへ引き継ぐところまで相談できます。
Scalaを書く人がいない案件や分散実行を無償で組みたい場面では見送る
反対に、次の場面ではGatlingを選びません。
チームにJVMの言語を書く人がおらず、JavaScriptやPythonが中心なら、k6かLocustのほうが保守を引き取ってもらえます。GatlingにもJavaScriptのSDKはありますが、日本語の情報はまだ少なく、困ったときに頼れる資料が限られます。また、負荷生成のマシンを何台も並べて結果を1つにまとめる分散実行は、Gatlingでは有償のEnterprise Editionの守備範囲です。無償のまま複数台で負荷を出したいなら、ワーカーの分散を標準で持つLocustが手早く組めます。
試験の対象が数百人規模の社内ツールで、一度だけ確かめれば済むなら、どのツールでも結果は変わりません。チームが一番読める言語のツールで、スモーク試験と平均負荷の試験を1回ずつ流せば十分です。
よくある質問
Gatlingについて、検索で多い質問に答えます。
Gatlingは無料で使えますか?
本体はオープンソースで、ローカルやCIで試験を流してHTMLのレポートを出すところまでは無料で使えます。有償なのはGatling Enterprise Editionで、複数の負荷生成マシンへの負荷の振り分け、試験結果の保存と比較、SLOの管理などを提供しています。最初は無償版で始め、負荷生成が1台で足りなくなった時点や、結果を組織で共有したくなった時点で有償版を検討する流れで問題ありません。
GatlingのシナリオはScalaで書かないといけませんか?
いいえ。3.16系ではScalaのほか、Java、Kotlin、JavaScript、TypeScriptで書けます。公式のデモ集にも言語ごとのプロジェクトが用意されており、DSLのメソッド名はほぼ共通です。違いは注入の書き方くらいで、JavaではinjectOpenとinjectClosedを使い分けます。Scalaは古い解説記事が多く情報を探しやすい一方、Scalaを書く人がいないチームではJavaで書くほうが保守を引き継ぎやすくなります。
GatlingとJMeterはどちらを選べばよいですか?
シナリオをコードで管理し、プルリクエストでレビューしてCIで流したいならGatlingです。画面で試験を組み立てたい、またはプラグインの多さを重視するならJMeterが候補になります。JMeterはGUIで作った試験がXMLのファイルになるため、差分のレビューがしにくい点がGatlingとの大きな違いです。どちらを選んでも、到着率で負荷を指定できるか、リクエストごとに合否の基準を書けるかの2点を先に確かめてください。
Gatlingのレポートはどこに出力されますか?
sbtでもMavenでも、既定ではtarget/gatlingの下に、シミュレーション名と実行日時を付けたディレクトリが実行ごとに作られ、その中のindex.htmlがレポートです。CIで実行する場合は、このディレクトリを成果物として保存しておくと、ビルドが失敗したときにグラフを確かめられます。Ctrl+Cで途中で止めた試験も、残ったsimulation.logから-roオプションでレポートを作り直せますが、最後の数秒のデータは欠けることがあります。
Gatlingで何人まで同時に負荷をかけられますか?
負荷生成マシンのCPUとネットワーク、シナリオの中身で上限が決まるため、ツールとしての固定の人数はありません。仮想ユーザーがスレッドを占有しない作りなので、1台から数千人規模の同時利用を再現しやすいのが特徴です。ただし、負荷生成側のCPUが先に張りつくと、狙った到着率を出せず結果が実際より良く見えます。試験中は負荷生成マシンのCPU使用率も記録し、足りなくなったらマシンを増やしてください。
関連記事
- k6とは?Grafana k6の使い方とインストール手順・負荷テストの書き方【v2.3対応】:同じ試験をJavaScriptで書き、Grafanaで結果を見たい場合の手順です
- Locustとは?Pythonで書く負荷試験の使い方・ramp-up・分散実行【2.46対応】:Pythonで書き、負荷生成を無償で複数台に分けたい場合の選択肢です
- JMeterでHTTP/2負荷テストを行う方法|BlazeMeter HTTPプラグインの導入と設定【3.2対応】:JMeterでHTTP/2の通信を再現して試験する方法です
- 負荷テストツール比較|Web・CPU・ネットワーク別の無料ツールとWindowsでの実行手順:Gatling以外も含めて、試験の対象からツールを選ぶときの比較材料になります
- SLOとは?SLA・SLIとの違いと設定方法をわかりやすく解説:assertionsに書いた応答時間の目標を、運用の目標値として引き継ぐ方法です