負荷テストについて / Apache JMeter

WEBシステムの負荷テストが必要になったので調べてみた、ついでに「Apache JMeter」ってツールを試す

作成日:2025-04-01, 更新日:2025-04-10

一般的なシナリオ

  • アクセス数による確認
  • ユーザーの挙動による確認

アクセス数による確認

任意のページに同時アクセスして確認。ある程度、通常のアクセス数を把握・想定する必要がある

  • アクセス増加の確認
    • 「急激なアクセス増加」の確認が「スパイクテスト
    • 「徐々にアクセス増加」の確認が「スケーラビリティテスト
  • 長時間のアクセスの確認
    • 「通常のアクセス数が長時間続く場合」の確認が「持続負荷テスト
    • 「多いアクセス数が長時間続く場合」の確認が「ピークトラフィックテスト

ユーザーの挙動による確認

一般的なECサイトを想定した場合、「ログインして、対象ページを開き、商品をカートにいれ、決済」を実行させて確認

  • 実際の動きにあわせた確認が「エンドツーエンドの負荷テスト

負荷テストで使う会員の問題

理想は...

  • 負荷テスト用に会員を用意
    →大量に登録できるような仕組みを別途用意 or がんばって手動で登録
  • 負荷テストが終われば削除
    →大量に削除できるような仕組みを別途用意 or がんばって手動で削除 or 削除せず放置 or DBの各テーブルをクリア
    ※会員の削除が論理削除なら物理削除のメソッドを別途用意

負荷テストを行う際の注意点

アクセス負荷をかけるので、環境に気をつける
→レンタルサーバなら各種制限のチェックは必要と思う

負荷テストを行うツール

JMeter、LoadRunner、Gatling、Locust、k6などがあるっぽい

Apache JMeter

JMeterってツールで負荷テストしてみる
※参考: 誰にも迷惑をかけずにJMeterを動かす一番シンプルなやり方

準備

  1. JAVAをインストしておく
  2. Apache JMeterのサイトからソースをダウンロード、解凍、実行
    ※「bin/jmeter.bat」を実行
    ※日本語化したいけど、分からないことがあったとき、どうせ英語サイトばっかりだから英語のままにしておく(「Options - Choose Language - Japanese」で日本語化)

テストの作成:お試し

  1. スレッドグループを作成
  2. 実行内容を作成(お試し)
  3. 結果の準備
  4. テストの保存、実行
  5. 結果の確認

スレッドグループを作成

「Test Plan」を選択し、「Edit - Add - Threads(Users) - Thread Group」でスレッドグループを作成

  • Number of Threads - 人数: お試しテストなんで「1ユーザー」
  • Ramp-up period - 全員が実行開始するにかかる時間: お試しテストなんで「1秒」
  • Loop Count - 実行回数: お試しテストなんで「1回」

実行内容を作成(お試し): httpリクエストの作成

「Test Plan - Thread Group」を選択し、「Edit - Add - Sampler - HTTP Request」でリクエストの設定

「Protocol、Server Name、PATH」を設定する
※「https://example.test/hoge」の場合、「Protocol - https, Server Name - example.test, PATH - hoge」。「Protocol - https, Server Name - example.test/hoge, PATH - [空白]」はダメっぽい

結果の準備

「Test Plan」を選択し、「Edit - Add - Listener - Aggregete Report」で結果を準備

テストの保存、実行

いったん、ここまでの内容を保存しておく

「File - Save」や「File - Save Test Plan as」

結果の確認

実行して、先ほど保存した「Aggregete Report」を確認

Graphite

「Aggregete Report」以外にとりえあず「Summary Report」「View Results Tree」「Backend Lister」も追加してテストを実行すると下記エラーがでてきた

2025-04-01 16:10:00,843 ERROR o.a.j.v.b.g.TextGraphiteMetricsSender: Error writing to Graphite: connect timed out
java.net.SocketTimeoutException: connect timed out
	at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method) ~[?:1.8.0_361]
...

Graphiteサーバーってヤツに接続できない...的なエラーらしい
そもそも、「Graphiteサーバー」って何?って調べたら監視ツールみたいなヤツらしい

さらに調べるとJMeterが「Graphiteサーバー」にリクエストをしているってトコまでは分かったけど、それを無効にしたい

調べると...有効にする方法はあるけど、無効にする方法が見つけられず...ガンバって調べたら「Aggregete Report」以外にとりえあず追加していた「Backend Lister」ってヤツが原因ということがわかった
→とりあえず、削除して解決

テストの作成:ログイン処理

ログインの負荷テストをつくってみる...挫折したけど...

  1. httpリクエストを追加: /login, get送信
  2. 正規表現抽出を追加: csrfトークンを取得
  3. httpリクエストを追加: /login/do, post送信

上記のような感じでいけるんだけど...送信前にAPIでバリデーションチェックを実行していて、csrfトークンがワンタイムトークンなんだなぁ...JMeterでもどうにか出来るっぽいけどタイヘンそうな感じなので挫折...

本来やりたい流れは下記のような感じ

  1. httpリクエストを追加: /login, get送信
  2. 正規表現抽出を追加: csrfトークンを取得
  3. httpリクエストを追加: /login/validation, post送信
  4. csrfトークンを再生成 ← ココがjavascriptで実行しているのでよく分らん
  5. httpリクエストを追加: /login/do, post送信

正規表現の抽出

「Edit - Add - Post Processors - Regular Expression Extractor」で設定
「Name of created variable」ってのが参照名になるので、httpリクエストのパラメータの値で使う
使うときは「${}」で囲んでやる必要がある

テストの作成: 同時アクセス

「5秒以内に500ユーザーがアクセスしてきて、1時間、サイトを利用した」とする
→タイマーをセットする必要がある

「Edit - Add - Timer」で「Constant Timer(一定時間ごと)」「Gaussian Random Timer(ランダム)」から設定

シナリオ: 2秒に一回アクセス

5秒以内に500ユーザーがアクセスしてきて、2秒ごとにアクセスを1時間繰り返した

「2秒に1回」で「1時間」なので「ループ回数 = 3600 / 2 = 1800回」

  • スレッド数 - 500人
  • Ramp-Up 期間(秒) - 5秒
  • ループ回数 - 1800回

Constant Timer

  • Thread Delay - 2000ミリ秒

シナリオ: 3~10秒にアクセス

5秒以内に500ユーザーがアクセスしてきて、3~10秒ごとにアクセスを1時間繰り返した

「3~10秒に一回アクセス」なので「平均 = (3+10)/2 = 6.5秒」
「1時間実行」なので「ループ回数 = 3600 / 6.5 = 554回」

  • スレッド数 - 500人
  • Ramp-Up 期間(秒) - 5秒
  • ループ回数 - 554回

Gaussian Random Timer(正規分布(ガウス分布))
「Deviation (偏差)」と「Constant Delay Offset (一定遅延オフセット)」を設定する必要がある
→「Deviation」がプラスマイナスの値で、「Constant Delay Offset」が基準の値となる

「3秒~10秒」なので、
・Deviation = ((10-3)/2)*1000 = 3.5*1000 = 3500ミリ秒
・Constant Delay Offset = ((3+10)/2)*1000 = 6.5*1000 = 6500ミリ秒
→6.5秒を基準(Constant Delay Offset)にプラスマイナス(Deviation)3.5秒

  • Deviation (偏差) - 3500ミリ秒
  • Constant Delay Offset (一定遅延オフセット) - 6500ミリ秒

→「6500ミリ秒 - 3500ミリ秒」~「6500ミリ秒 + 3500ミリ秒」で「3~10秒」となる

テストの結果

設定次第で色々と異なるらしいので、よくわからん
とりあえずThroughput(スループット)ってが重要な気がする

Throughput

Throughputってのが毎秒のリクエスト数になるそうだ
→想定ユーザー数、各ユーザーの滞在時間・閲覧ページ数などから秒単位のリクエストを算出。それより大きいなら問題なし。小さいなら高負荷になっていると思われる

小規模なECサイトなら、秒間数リクエストから十数リクエスト。比較的少数の同時ユーザー(数十人から数百人)を想定した値
※Throughputは10/sec~15/secあたり

サーバ負荷の確認

とりあえずサーバ負荷を知りたいのでサーバ側でシステムのパフォーマンスを監視するのが良さげ
linuxでリアルタイムで目視するなら「top」、あとで確認するなら「vmstat」
→目視で異常を発見できる自身は皆無なので「vmstat」を使う

▼毎秒データを保存。終了したければ「ctrl + c」

$ vmstat 1 > vmstat_20250401_1420.txt

▼毎秒データを保存。10分で処理終了

$ timeout 600 vmstat 1 > vmstat_20250401_1420.txt

見るところ

  • 「cpu」の「wa(wait time)」が高いとディスクやネットワークの問題あり
  • 「swap」の「si(swap in)」と「so(swap out)」が高いとメモリ不足の問題あり

メモ

ニュアンスが異なってたり、微妙に違ったり、完全に間違ってたりするかもしれないけど、ひとまず...とりあえず自分の中で確定させて必要に応じて認識を改める

  • 負荷テストにおける「スレッド数」とは「ユーザー数、セッション数」と同義
  • Ramp-Up 期間(秒)は、スレッドを一つずつ開始するときの時間差
    ※「実行開始時間 = Ramp-Up 期間(秒) ÷ スレッド数」

    • スレッド数1、Ramp-Up期間10秒なら0秒後に実行
    • スレッド数2、Ramp-Up期間10秒なら0秒後と5秒後に実行
    • スレッド数5、Ramp-Up期間10秒なら0秒後から2秒ごとに実行

    ループ回数は同じ処理を繰り返すだけだから、終了時間は処理内容によるのでケースバイケース
    → 一般的なECサイトの負荷テストで「シナリオ: セール開始直後に1000人が同時アクセス」を想定した場合、「スレッド数1000、Ramp-Up期間1秒、ループ回数1」のような感じ

  • 「エンドツーエンドの負荷テスト」と「スパイクテスト、スケーラビリティテスト、持続負荷テスト、ピークトラフィックテスト」を組み合わせるのが基本
    • 「持続負荷テスト」は運用当初のサービスが軌道に乗る前を想定
    • 「ピークトラフィックテスト」はサービスが軌道にのった後を想定
    • 「スパイクテスト」は運用スタート時や何かしらの告知・宣伝をしたときを想定
    • 「スケーラビリティテスト」は炎上したり、告知・宣伝の影響が継続しているときを想定
  • 負荷テストの結果を見るときにとりあえず抑えておくべき点
    • Throughput:
      判断をする前に想定ユーザー数、各ユーザーの滞在時間・閲覧ページ数などから秒単位のリクエストを算出。
      それより大きいなら問題なし。小さいなら高負荷になっていると思われる
    • vmstatで確認する点:
      ・「cpu」の「wa(wait time)」が高いとディスクやネットワークの問題あり
      ・「swap」の「si(swap in)」と「so(swap out)」が高いとメモリ不足の問題あり