もしかすると自分以外の人にはあんまり参考にならないかもしれないJMeterのTips

負荷テストツールJMeterのTipsです。

最近は[gatling](http://gatling.io/)とか[iago](https://github.com/twitter/iago)あたりが流行ってますが、情報の入手のしやすさや、豊富なプラグインによる機能面での優位性からJMeterはまだまだ使われるのではないかなぁと思っています。

ということで、自分が何度か負荷テストを行った際に学んだJMeterのTipsをまとめたいと思います。
タイトルの通り、かなり独自路線で学んだ点が多いのであんまり参考にならないかもです。

なお、ある程度は使い方を知っている方を対象にしています。
ある程度というのは、HTTPサンプラーをベースとしたスクリプトをTest Script Recorder(旧HTTP Proxy Server)を使って流した処理を元に作ったことがあって、CSV等から読んだりしたユーザごと等の可変パラメータを使ってひと通りのテストをしたことがあったりする程度です。

# スクリプトはGUIで作って実行はCUIで行う

テストスクリプトの作成はプロキシ(Test Script Recorder)を通してGUIで行うのが楽ですが、テスト実行はコマンドラインで行うと色々便利です。例えばグラフの作成やサーバの性能情報の収集などの前処理・後処理等をラップしたスクリプトを作って一発で必要な情報をまとめることができます。
ただしリアルタイムでListenerの結果が見れなくなる等のデメリットもあります。

自分の場合は、

* 複数のテストケースを実行する必要がある
* 各テストケースごとにそれぞれ試行錯誤しながら実施する
* テストの結果はエビデンスとしてまとめておく必要がある

といった点からコマンドラインから実行するメリットが大きいためコマンドラインから実行することが多いです。
特にエビデンスを求められる場合、毎回手動でまとめてからグラフにしたりするのが面倒だったりします。

ちなみに自分の場合はコマンドラインからの実行は以下のようにしています。(Linuxの例)

```bash:jmeter-wrapper.sh
#!/bin/bash

# 結果保存ディレクトリの設定
readonly RESULT_DIR=/path/to/result/$(date '+%Y%m%d%H%M%S')
mkdir ${RESULT_DIR}
readonly JMETER_OUTPUT_FILE=${RESULT_DIR}/jmeter-output.jtl
readonly JMETER_TEST_FILE=jmeter-test-case.jmx

# JVM引数(GCログ出力設定)をしてからjmeterをコマンドライン実行
export JVM_ARGS="-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:${RESULT_DIR}/gc-jmeter.log"
/path/to/jmeter.sh -n -t ${JMETER_TEST_FILE} -l ${JMETER_OUTPUT_FILE}\
```
JMETER_TEST_FILEはGUIの画面から作ったjmeterのテストファイル(jmx)、JMETER_OUTPUT_FILEは結果を保存するjtl(フォーマットはデフォルトではcsv)ファイルです。
なお、jtlファイルはJMeterのListenerで読み込むことができます。
そしてcsvのため自由に加工することができます。

## 自動でグラフ化する

コマンドライン実行でcsvとして結果を出力することでresponseタイム等の値を自由にグラフ化できます。常時流しっぱなしにしてfluentd等でどこかに溜めておいて可視化するのも良いかと思います。自分の場合は単発のテスト単位でグラフを出してエビデンスとしてまとめたりすることが多いです。

そんなときは[JMeterPluginCMD](jmeter-plugins.org/wiki/JMeterPluginsCMD/)を使うと自動でグラフ化することができます。
上記のスクリプトの続きから書くと以下のようになります。

```bash:jmeter-wrapper.sh
(前略)
/path/to/jmeter.sh -n -t ${JMETER_TEST_FILE} -l ${JMETER_OUTPUT_FILE}\
java -jar /path/to/apache-jmeter-2.11/lib/ext/CMDRunner.jar --tool Reporter --generate-png ${RESULT_DIR}/responsetime.png --input-jtl ${JMETER_OUTPUT_FILE} --plugin-type ResponseTimesOverTime --width 1280 --height 800 --include-labels "labelname"

```
上記ではテスト実行後に${RESULT_DIR}にレスポンスタイムのグラフがresponsetime.pngとして出力されます。スクリプト内で実行しているためテストケースを実行する度に自動でグラフが生成されます。

なお、include-labelsを指定すると特定のサンプラーやスレッドグループのレスポンスタイムのみ表示することができます。また、CMDRunner.jarはJMeterのインストールディレクトリ以下のlib/extに配置する必要があります。

その他の指定可能なオプションは上記の公式サイトをご参照ください。

## 性能統計情報を自動で収集する

負荷テストを実行した場合、テスト期間の性能統計情報(CPU使用率とかgcログの値とか)も合わせて取得する必要があったりします。これも定常的にはZabbixのグラフがあったり、fluentdで集めて可視化してたりするかもしれません。しかしテスト期間に絞ったデータをひとまとめにしておくとエビデンスとして見せたりする場合は便利です。

こんなときもコマンドラインで実行していれば簡単に収集することができます。
自分の場合は以下のようにJMeterの実行前にログ収集の仕組みを動かしています。

```bash:jmeter-wrapper.sh
(前略)
# ローカル(jmeter実行端末)のvmstatを取得
vmstat -n -S m -a 5 | awk '{print strftime("%H:%M:%S"), $0} { system(":") }' > ${RESULT_DIR}/vmstat-jmeter.log &
# ログ収集スクリプトを実行
ssh $APP_SERVER "~/work/collect.sh" &
/path/to/jmeter.sh -n -t ${JMETER_TEST_FILE} -l ${JMETER_OUTPUT_FILE}\
(後略)
```
事前にpasswordless sshを設定しておく必要があります。
また、性能情報を収集するサーバには、事前に収集スクリプト(上記のcollect.sh)を置いてます。(ちょっとダサいので仕組みを変えたら更新します)

なお、collect.shの中身は以下のようなシンプルなものです。

```bash:collect.sh
tail -n 0 -f /path/to/gclog.log > ~/work/logs/gc-app.log" &
vmstat -n -S m -a 5 | awk '{print strftime("%H:%M:%S"), $0} { system(":") }' > ~/work/logs/vmstat-app.log &
```
そしてスクリプトの最後にsshでターゲットサーバで実行している上記のログ収集プロセスを殺してscp等でログを収集すれば、テスト単位で一つのディレクトリにデータが集まり、加工等もスクリプト内で行うことができます。

# プラグインを知る

JMeterには豊富な[プラグイン](http://jmeter-plugins.org/)が用意されています。
プラグインを使うと標準では提供されていないリスナーやサンプラーが使えるようになります。
以前は個別にプラグインが作られていましたが、数年前くらいから上記のプラグインページに統合されて探しやすくなりました。
自分が使ったことがあるものを中心にいくつか紹介したいと思います。

## コマンドラインからグラフ化するプラグイン

既に上記で紹介済みですが、[JMeterPluginsCMD](http://jmeter-plugins.org/wiki/JMeterPluginsCMD/)を使うとコマンドラインからグラフを作成できます。
上述の通りですが、テスト単位で結果をまとめる際などに重宝します。

## 条件指定でテストを自動停止する

[AutoStopListener](http://jmeter-plugins.org/wiki/AutoStop/)を使うと一定の基準に従って自動でテストを停止することが可能です。
現在はレスポンスタイム、レイテンシ、エラー率によって止めることができます。

試行錯誤しながらテストをしている場合、レスポンスタイムが一定以上超えたら既にエビデンスとして使えないのでチューニング等を行なってから再テストしたいといったことがあります。
そんな場合に必要以上にテストを流しっぱなしにせず自動で止めることができるので便利です。


## クライアントサイドのテストもできるWebDriverプラグイン

以前のJMeterではHTTPサンプラーでサーバサイドの負荷を測るだけでクライアントサイドのテストはできないものでした。
最近の(2013年6月から)JMeterには[WebDriverプラグイン](http://jmeter-plugins.org/wiki/WebDriverSampler/)が追加され、クライアントサイドの負荷も測定できるようになりました。


とりあえずこんなところです。
あとでもう少し詳細や種類を追加するかもしれません。
なお、基本的には上記のプラグインページにまとまってはいますが、独自のプラグインというのも存在しますので、上記のページに限らずググってみるとお求めのプラグインが見つかるかもしれません。


# Javaのサンプラーを拡張して独自のサンプラーを作る

既存のサンプラーやプラグインでは不十分な場合は独自のサンプラーを書くことができます。

自分の場合は、Javaのサンプラーを拡張して任意のJavaの処理の時間を計測するサンプラーを作ることが多いです。例えば独自に書いた、または、独自のフレームワーク等を使ったJavaの処理があった場合にもJMeterからテストできるようになったりします。

そうすることで、例えばロングランテストを実施する際にオンラインの処理と独自のバッチ処理を合わせてテストできるようになります。そして、計測した結果はJMeterの機能で集計やグラフ化ができます。

作成は以下の手順で行います。

* Eclipseでプロジェクト(MavenプロジェクトやJavaプロジェクト等)を作成
* pom.xmlを編集して必要なjarを読み込むようにdependencyを追加
(Mavenプロジェクトとして作った場合です。通常のJavaプロジェクトで作った場合は、ビルドパスを編集します。)
 追加するjarは以下の通りです。他にも依存jarがあれば追加してください。

	```:追加するdependency
	<dependency>
		<groupId>org.apache.jmeter</groupId>
		<artifactId>ApacheJMeter_java</artifactId>
		<version>2.11</version>
	/dependency>
	```

* サンプラーを作成
DemoWorkerクラスのworkメソッドの時間を測定するDemoSamplerの例を示します。

	```java:DemoSampler.java
	package demo.jmeter.extend.sampler;

	import org.apache.jmeter.config.Arguments;
	import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
	import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
	import org.apache.jmeter.samplers.SampleResult;

	/**
	 * 任意の処理を実行するようにJMeterのSamplerを拡張したサンプル
	 */
	public class DemoSampler extends AbstractJavaSamplerClient {

		// パラメータのキーを定義
		private static final String REQUEST_DATA_HOGE = "requestDataHoge";
		private static final String REQUEST_DATA_FUGA = "requestDataFuga";

		/* (non-Javadoc)
		 * デフォルトのパラメータを設定する
		 */
		@Override
		public Arguments getDefaultParameters() {
			// デフォルトパラメータの設定
			Arguments defaultParameters = new Arguments();
			defaultParameters.addArgument(REQUEST_DATA_HOGE, "hogehoge");
			defaultParameters.addArgument(REQUEST_DATA_FUGA, "fugafuga");
			return defaultParameters;
		}

		public SampleResult runTest(JavaSamplerContext context) {
			SampleResult result = new SampleResult();
			try {
				// パラメータを設定(JMeter側の設定方法は後述)
				String paramHoge = context.getParameter(REQUEST_DATA_HOGE);
				String paramFuga = context.getParameter(REQUEST_DATA_FUGA);

				// 時間計測開始(1)
				result.sampleStart();

				// 計測したい任意の処理
				String response = new DemoWorker().work(paramHoge, paramFuga);

				// 時間計測終了(2)
				// (2) - (1) の時間がJMeterのレスポンスタイムとして取得できる
				result.sampleEnd();
				// JMeterの取得結果を成功にする
				result.setSuccessful(true);
				// JMeterのレスポンスコードをOK(200)に設定する
				result.setResponseCodeOK();
				// JMeterで表示されるレスポンスデータの中身を詰める
				result.setResponseMessage("Success!! " + response);
			} catch (DemoException e) {
				// 時間計測終了(2)
				// (2) - (1) の時間がJMeterのレスポンスタイムとして取得できる
				result.sampleEnd();
				// JMeterの取得結果を失敗にする
				result.setSuccessful(false);
				// JMeterのレスポンスコードをOK(200)以外の数字に設定する
				result.setResponseCode("500");
				// JMeterで表示されるレスポンスデータの中身を詰める
				result.setResponseMessage("Error!! " + e.getMessage());
			}
			return result;
		}
	}
	```
例のため最低限のものしかセットしていません。
DemoWorker側は省略しますがJavaの任意の処理を測定できることが分かるかと思います。
* 作成したプロジェクトをjarとしてエクスポートします。
* JMeterのインストールフォルダのlib/ext以下に上記でエクスポートしたjarを入れます。
もしpomに他にもjarを追加していれば、それらのjarファイルも同じところ(lib/ext以下)に入れます。
* JMeterを起動します。
 * スレッドグループを追加します。
 * 追加したスレッドグループにJavaRequestを追加します。
 * classnameとして先ほど作成したクラスの名前を選択します。
 * パラメータは適切に設定してください。
 * CSV Data Set Configでcsvの値を読み込んで使う等のことももちろん可能です。
 * 結果確認用にスレッドグループにResultTree等のリスナーを追加します

![jmeter02.png](https://qiita-image-store.s3.amazonaws.com/0/36003/91c7d737-31e3-fe48-59e2-93c3b25419c2.png)

* これでJmeterを実行すれば独自処理の時間もリスナーに表示されるようになります。

以上です。
また何かあれば追加・更新等しようと思います。