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

jmeterでやるべきこと云々 その2

Java

こんにちは、古川です。

IMG_4652


さて、今回はjMeterで負荷テストをしたとき、やってよかったことの続きを紹介します。 
高負荷のテストは複数のプロセスで同時に立ち上げ実行させる

前回の記事

前回紹介した2つのやり方で、ある程度性能が発揮されるんですが、jmeterの仕様が原因なのか、

javaが原因なのか正直はっきりしていませんが、1台のサーバでフルに発揮してくれません。

そこで、「複数のプロセスを立ち上げ、プロセスごとに少ないスループット数で動かす」といった
方法をとってサーバの性能をフルに使うようにしました。

設定の仕方についてですが、前回と同じようにbin/jmeter.binをいじります。

bin/jmeter.binにつぎのような個所(jmeter本体を実行している箇所)がありますが、

%JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS%

このように変更しました。

rem 現在時刻YYYYMMDDHHMMSSを取得
set HMS=%TIME::=%
set HMS=%HMS: =0%
set HMS=%HMS:.=%
set YMDHMS=%DATE:/=%%HMS%

rem -Jオプションから作成したいフォルダを設定
set TYPE=unknown_type
set OPTION_TYPE=NO_OPTIONS
:LOOP
IF "%1"=="" (goto :END)
IF "%1"=="-t" (
  shift
  shift
  goto :LOOP
)
IF "%1"=="-Jname" (
  set TYPE=%2
) ELSE (
  IF "%OPTION_TYPE%"=="NO_OPTIONS" (
    set OPTION_TYPE=%2
  ) ELSE (
    set OPTION_TYPE=%OPTION_TYPE%_%2
  )
)

shift
shift
goto :LOOP

:END

set OUTPUT_FOLDER="result\%TYPE%\%OPTION_TYPE%\%YMDHMS%"
mkdir %OUTPUT_FOLDER%

rem jmeter 8プロセス分 同時実行
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_2.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_2.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_3.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_3.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_4.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_4.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_5.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_5.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_6.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_6.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_7.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_7.csv -Jymdhms=%YMDHMS%
start "" /wait %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_8.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_8.csv -Jymdhms=%YMDHMS%

rem 最後実行したプロセスが最後終わるとは限らないため、保険として10秒間待つ
TIMEOUT /T 10

結構長くなってしまいました・・・1つ1つ説明します。

まずは、この個所です。

rem jmeter 8プロセス分 同時実行
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_2.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_2.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_3.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_3.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_4.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_4.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_5.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_5.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_6.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_6.csv -Jymdhms=%YMDHMS%
start "" %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_7.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_7.csv -Jymdhms=%YMDHMS%
start "" /wait %JM_START% %JM_LAUNCH% %ARGS% %JVM_ARGS% -Xloggc:%OUTPUT_FOLDER%\gc_8.log -jar "%JMETER_BIN%ApacheJMeter.jar" %JMETER_CMD_LINE_ARGS% -n -l %OUTPUT_FOLDER%\result_8.csv -Jymdhms=%YMDHMS%

rem 最後実行したプロセスが最後終わるとは限らないため、保険として10秒間待つ
TIMEOUT /T 10

これをみて、「あれ??繰り返し書かないの??」と突っ込まれますが、
windowsのコマンドシェルで繰り返しを書くのが正直面倒だったため、
こんな感じにやってしまいました。ごめんなさい・・・・OTL

それはさておいて、上記ではつぎのことをやっています。

CUIモードで8つ同時に起動させている
  •  -n オプションつけることで、CUIモードで起動させています。
  • start "" コマンド を並べることで並列実行させています。
  • 最後は start "" /wait コマンド にすることでこのプロセスが終わるまで実行を待ちます。これがないと先の処理に進んでしまいます。
  • 最後に実行したプロセスが最後に終了するとは限らないので、保険で TIMEOUT /T 10 いれてます。
GCのログと実行結果をそれぞれのファイルで出力
  • GCのログはgc_x.log
  • 実行結果はresult_x.log
なお、GCのログや実行結果の出力先は、変数OUTPUT_FOLDERを用いていますが、
OUTPUT_FOLDERはつぎの方法で定義しています。

rem 現在時刻YYYYMMDDHHMMSSを取得
set HMS=%TIME::=%
set HMS=%HMS: =0%
set HMS=%HMS:.=%
set YMDHMS=%DATE:/=%%HMS%

rem -Jオプションを取得し、フォルダを設定
set TYPE=unknown_type
set OPTION_TYPE=NO_OPTIONS
:LOOP
IF "%1"=="" (goto :END)
IF "%1"=="-t" (
  shift
  shift
  goto :LOOP
)
IF "%1"=="-Jname" (
  set TYPE=%2
) ELSE (
  IF "%OPTION_TYPE%"=="NO_OPTIONS" (
    set OPTION_TYPE=%2
  ) ELSE (
    set OPTION_TYPE=%OPTION_TYPE%_%2
  )
)

shift
shift
goto :LOOP

:END

rem 出力先のフォルダを定義・生成
set OUTPUT_FOLDER="C:\path\to\result\%TYPE%\%OPTION_TYPE%\%YMDHMS%"
mkdir %OUTPUT_FOLDER%

これも、あまりいけてないコードですいません・・・(shellだったらもっと綺麗にかけるかな・・・)

ここでは-Jから始まるオプションの内容をもとにフォルダを生成するようにしています。
具体的には、このようにコマンドを入力すると、つぎのような感じでフォルダが生成されます。

jmeter_cui_multi.bat -t "C:\path\to\test.jmx" -Jthreads=8 -Jloop=240 -Jname=type
→C:\path\to\result\type\8_240\YYYYMMDDhhmmss(タイムスタンプ)

rem -Jnameがなければunknown_typeにしています
jmeter_cui_multi.bat -t "C:\path\to\test.jmx" -Jthreads=8 -Jloop=240
→C:\path\to\unknown_type\8_240\YYYYMMDDhhmmss(タイムスタンプ)

rem -Jname以外がなければ、2階層目がNO_OPTIONになります
jmeter_cui_multi.bat -t "C:\path\to\test.jmx" -Jtype=type
→C:\path\to\type\NO_OPTION\YYYYMMDDhhmmss(タイムスタンプ)

あとは実行したいテストデータ(jmx)を開けて、
スレッドグループのスレッド数・ループ数をつぎのように設定します。

コマンド実行時にスレッド数・ループ数はそれぞれ-Jthreads, -Jloopで指定できるようにします。

さらにデフォルトも指定することで、GUIで手動で動作確認したいときにいちいち変更する必要をなくします。

# スレッド数(変数指定がなければデフォルト1)
${__P(threads, 1)}

# ループ数(変数指定がなければデフォルト1)
${__P(loop, 1)}

これで、負荷をかける側のサーバのスペックをフル活用テストできるようになりました。
また、コマンドでテストでき、結果も分けて管理できるようになりました。

ただし、jmeter.batに直接書き換えてしまうと、GUIが使えなくなるので、

jmeter.batをコピーして編集するなりして対応してください。




定数スループットタイマーを用いること

ここまでで負荷テストができるようになりましたが、つぎのことに気付きます。
  • 指定したリクエスト量をキープしながら負荷テストがうまくできていない。
というのも、jMeterではスレッドグループのループ数とスレッド数だけ指定しても、ある程度決まった
リクエスト量で処理してくれますが、比較的短い時間で終わる(1秒未満)処理になると、
スレッド数以上に分間リクエスト量が増えてしまうからです。

たとえば、スレッド数50で、平均実行時間が0.7秒だと分間リクエスト数は4286、
0.5秒だと6000という感じになります。

こうなると平均実行時間を考慮しないといけません。また、平均実行時間も負荷をかける側
の状態によって変化してしまいます。

このままでは一定のリクエスト量をキープしてテストすることができません。

そこで、定数スループットタイマーをつかうことで解決していきました。

設定方法については、まず、jMeterのスレッドグループを右クリックして、
「タイマー」→「定数スループットタイマー」で作成します。こんな感じになります。

jmeter1


定数スループットタイマーで設定できる項目はつぎのようになっています。

ターゲットスループット(サンプル/分):分間実行するサンプル数
  ・ ここで指定する数値は<b>1分間に実行するサンプラー数</b>という点に注意してください
  例)スレッドグループに2つのサンプラーがあり、分間600リクエスト出したい場合は1200

Calculate Throuphput based on: スループットを計算する基準を指定できます。
  ・ここはall active threads in current thread groupにします。
  →これでスレッドグループ単位のサンプラーの数を基準に一定のスループット数で実行してくれます。


jmeter2

そして、ターゲットスループット(サンプル/分)はつぎのように設定します。
これで、-Jthroughputオプションでスループット数を指定できるようになります。

# ターゲットスループット数(変数指定がなければデフォルト540)
${__P(throughput, 540)}
 
ここまでの設定で、一定のリクエスト量をキープして負荷をかけることができるようになりました。


 

実行時間を決めて実行するようにする

ここまでやって、DBの負荷テストが十分できる状態になりました。

しかし、まだ問題が残っています。

それは、負荷の程度が大きくと全部完了するのに想定以上に時間が掛ってしまうことです。

4分間の結果だけほしくて、ループ数を240にしても、負荷によっては4分以上
かかったりすることが起こってしまいます。

これでは負荷テストをする時間がもったいないです。

そこでここで紹介する設定をすることで、「4分経過したら終了する」なんてことが簡単にできます。

やり方ですが、スレッドグループを開き、スケジューラにチェックを入れます。

するとこんな感じになります。


jmeter3

 
そこで、持続時間(秒)の項目がありますが、そこに 240 と指定するだけです。

これで4分経過したら強制終了させることができます。

ここでの注意点としては開始時刻と終了時刻はかならず過去の時間にしておいてください。

過去の時間にしておくことで任意のタイミングで実行できるようになります。

そうでないと開始時刻と終了時刻が有効になってテストがうまくいきません。




以上です。

負荷テストでは試行錯誤しながらですが、さきほど紹介した内容をやってきました。

また、役に立つようなネタがあればまた上げていきますので、そのときはよろしくお願いします。