JMeterっていちいち調べなきゃでめんどくさいなって人（ぼく）用のメモ

仕事で負荷試験をやるためにJMeterを使いました。

いろいろ調べながら使ってみるのはいいのですが、すごくたくさんのことができるのはわかるんですけど、出来ること多すぎて何を使えばいいのか、やりたいことはどうやって実現すればいいのかよくわからない、、、という状態に陥りました。。。

そこで、Webアプリ初心者（負荷テストなんて以ての外）のこのぼくが、いろいろ使いながら調べたことをまとめておきます。
基本自分用のメモを体裁ととのえたもので、スクショとったりして猿でもわかるレベルにできたらよかったんですが、使いながら読むメモと思ってご愛敬。

**とにかく使いながら覚えたいけど何度もググったり英語のドキュメント見たりはめんどくさいなというずぼらさん向け。**

余談ですがオブジェクト指向はびこる昨今、プログラムソースだと細かくかき分けて、カテゴリや処理ごとにファイルも別々なんてのが美しいとされますが、ドキュメントってのは個人的に、「これ見りゃ（だいたい）ぜんぶわかる！」ってのが美しいと思います。

だいぶ長い記事になってますが、決して、記事の分割がめんどくさかったとかではないのです。
余談おわり。

# JMeterの導入

環境は Windows 10 です。

## 1. ダウンロードと展開
 
以下のURLからダウンロード。
<http://jmeter.apache.org/download_jmeter.cgi>

* **Binaries** から zip 形式のファイルをダウンロードする(tgz とかでもいいけど)。
* 圧縮ファイルを任意の場所に展開する。
* 実行するのは bat ファイルのためインストーラ等の実行はなし。

## 2. 起動

展開したフォルダの **apache-jmeter-5.1.1\bin\jmeter.bat** を実行。

以下のエラーが出た場合、Javaが入っていないので、ダウンロードして入れる。

```
Not able to find Java executable or version. Please check your  Java installation.
errorlevel=2
続行するには何かキーを押してください . . .
```

### 2.1 言語切り替え

初回はツールバー等がすべて英語で起動される。
変更する場合は`Options->Choose Language->Japanese`で日本語に設定。

- JMeterを再起動すると英語に戻る。日本語にするには再設定が必要。
- 以降の説明は、日本語表示のものとして記述。

# JMeter の利用
## 1. スレッドグループの作成

### 1.1 新規スレッドグループの作成
1. 画面左側`テスト計画（TestPlan）`を右クリック。
2. `追加`をクリック。
3. `Thread(Users)`をクリック。
4. `スレッドグループ`をクリック。

* スレッドグループ
文字通りスレッドの立ち上がる単位でひとつのシナリオのかたまり。

### 1.2 スレッドグループの設定
左側のウインドウで作成したスレッドグループを選択すると内容を設定できる。

スレッドグループは複数定義すると並列実行される。

|項目|意味|
|:--|:--|
|**名前**|スレッドグループ名|
|**コメント**|コメント|
|**サンプルエラー後のアクション**|サンプルエラーが発生した場合のふるまいを定義。<br>- 続行<br/>- Start Next Thread Loop<br>- スレッド停止<br>- テスト停止<br>- Stop Test Now|
|**スレッドプロパティ**|以下を設定する。<br>- スレッド数<br>- Ramp-Up期間(秒)<br>- ループ回数

## 2. 操作の記録
JMeterのテストシナリオ作成は、HTTPリクエストを手動で1つずつ記述する方法と、ブラウザの操作を自動で記録する方法がある。
ただし、以下の場合は、本手順による操作の記録ができない。
  * https を利用する環境
  別のツールを使えば可能らしいが、環境によってはあまりいろいろなプログラム入れたくない（禁止されてるとか、そういうの確認するのがめんどくさいとか）場合も多いでしょう。

操作記録の手順は以下。

### 2.1 記録コントローラーの追加
1. 対象のスレッドグループを右クリック。
2. `追加`->`ロジックコントローラー`->`記録コントローラー`を選択。
3. 記録コントローラーの情報（名前、コメント）を入力。

### 2.2 HTTP プロキシサーバの設定
1. 対象のTestPlan（スレッドグループでもいいけど）を右クリック。
2. `追加`->`Non-Test エレメント`->`HTTP プロキシサーバ`を選択。
5. `ポート番号`に任意のポート番号（使われてないもの）を入力。
6. `Test Plan Creation`の`対象となるコントローラ`に、作成した記録コントローラを選択。

- Non-Test エレメントは追加したのと同階層以下のすべてを対象とする。
要するに、指定したい記録コントローラと同じか上位の階層に配置すること。

### 2.3 プロキシの設定(ブラウザ側)

- ここではIEで説明。chromeなどでも同様にネットワーク設定からプロキシの設定をすること。

1. IE をひらく。
2. インターネットオプションの`接続`->`LANの設定`。
3. `LAN にプロキシサーバーを...`にチェック。
4. `アドレス`に localhost、`ポート番号`にHTTP プロキシサーバのポート番号を入力。
5. `OK` 押下で設定を完了する。

ここまでで、記録の準備は完了。

### 2.4 操作の記録

- ローカル環境で試験する場合は MEMO(本ページの下のほう）の「ローカル環境で操作を記録する場合」に注意。

1. 記録の開始
**4.2**で設定した、HTTPプロキシサーバの画面で`開始`を押下すると、操作の記録が開始される。
  ※`開始`を押すと英語いっぱい書いたダイアログが立ち上がるが`OK`でよい
  ※記録が開始すると、プロキシサーバ操作用のミニ画面が立ち上がる。
2. ブラウザで任意の操作
当たり前だけど、記録したい操作以外はやらない。
3. 記録したい操作が完了したら、HTTPプロキシサーバの画面（またはミニ画面）で`停止`を押下する。
※完了したら、ブラウザ側のプロキシ設定は解除。

### 2.5 記録した操作の確認
JMeter画面で、記録コントローラが展開できるようになっているので展開。
HTTPリクエストごとに番号が振られているので、不要なリクエストがあれば消す（faviconとか？）。

## 3. 記録した操作の実行

### 3.1 リスナーの追加

実行結果を表示するためリスナーを追加する。

1. 対象のスレッドグループを右クリック。
2. `追加`にカーソルを合わせる。
3. `リスナー`にカーソルを合わせ、任意のリスナーを選択。


> - リスナーは「TestPlan」「スレッドグループ」「サンプラー」ごとに追加できる。
-  サンプラーに追加した場合は、該当サンプラーの結果だけが取得される。
- スレッドグループに追加した場合は、該当スレッドグループ内のすべてのサンプラーの結果が取得される。
- TestPlanに追加した場合は、すべてのスレッドグループ内のすべてのサンプラーの結果が取得される。


リスナーは複数追加可能。
いろいろ使ってみたけど押さえるのは以下くらいでよいのでは。

|リスナー|説明|
|:--|:--|
|統計レポート|各リクエストに要した時間や転送速度などが、実行回数に応じて平均値とかで表示される。<br>テスト結果についてはほぼこれでいいと思う。|
|結果をツリーで表示|実行順や実行結果、リクエスト/レスポンスの内容がわかる。これはこれで優れものだと思う。<br>ただし、これ自体がけっこうCPUを食うらしく、テスト作成時のデバッグツールとして利用し、テスト実施時は削除するか、右クリックから無効にしておくこと。|

`結果をツリーで表示`もそうだが、リスナーの中には（JMeterを実行するマシンの）資源を食いまくるものがあるため、有効にしたままテストすると、大規模なテストになるほど正しい結果が取れない（最悪動かない）こともあるらしい。注意。

削除しないまでも右クリックで簡単に`無効`にできる。

### 3.2 記録した操作の実行
画面上部の緑の▷押下で実行。

> - スレッドグループが複数ある場合は並列実行される。
- 並列実行したくない場合はスレッドグループを開き、「各スレッドグループを別々に実行」にチェックを入れる。
- 複数スレッドグループを作成したが、テストのデバッグ等、特定のスレッドグループだけを実行したい場合がある。
  その場合は、実行しないスレッドグループを右クリックで無効にするか、実行したいスレッドグループを右クリックし「開始」で実行する。


### 3.3 結果の確認
1. 登録したリスナーを開くことで結果が確認できる。
統計レポートはCSV形式での保存も可能。

リスナーの内容は手で消さないとリセットされない。
新規にテストを実行する場合は画面上部の歯車と箒のボタンで削除すること。


> JMeterはレスポンスコードが200以外だった場合は該当するリクエストの結果が異常だったものとして動作する。
そのため、処理内容によっては「統計レポート」の「Error %」や、「結果をツリーで表示」の内容が期待と違うかもしれない。レスポンスボディ等の中身を確認しておくこと。


---
# MEMO
## 統計レポートの各項目の意味
わかる人はパッと見でわかっちゃうんだろうなぁ。
ここに書いてあるのは[公式](https://jmeter.apache.org/usermanual/component_reference.html#Aggregate_Report)見て「なんとなくこんなもんだろ」って程度。

|項目|意味|
|:--|:--|
| **Label** |HTTPリクエスト名|
| **# Samples**|リクエストを実行した回数<br>（スレッド数×ループ回数）|
| **Average**|各リクエストの応答までの時間の平均|
| **Median**|データを小さい方から大きい方まで並べた中のちょうど真ん中に位置するデータの値（中央値）|
| **90% Line**|90パーセントタイル<br>発行したリクエストのうち、90%はこの時間より早く終わったよという時間。テストで想定していない何かしらの要因で時間がかかってしまったリクエストを検証の対象からはずす目的で参照する。|
| **95% Line**|95パーセントタイル（よくわかってない）<br>発行したリクエストのうち、95%はこの時間より早く終わ以下略|
| **99% Line**|99パーセントタイル（よくわかってない）<br>発行したリクエストのうち、99%以下同文|
| **Min**|一番短かったリクエストの時間|
| **Max**|一番長かったリクエストの時間|
| **Error %**|エラーになったリクエストの割合|
| **Throughput**|秒間何件のリクエストが処理できたか|
| **Received KB/sec**|平均転送量（受信）|
| **Sent KB/sec**|平均転送量（送信）|

## ローカル環境で操作を記録する場合

localhostでアプリケーションサーバを立ち上げていると、プロキシサーバの設定をしてもJMeterがHTTPリクエストを記録してくれない。
アプリケーションサーバをlocalhostではなくIPアドレスで立ち上げるように設定する。

WindowsでのIPアドレス確認方法は、コマンドプロンプトで`ipconfig`実行。
localhost(127.0.0.1)以外の利用できるIPを利用する。

## HTTP認証を通したい

TestPlanまたはスレッドグループを右クリックし、`追加`->`設定エレメント`->`HTTP認証マネージャ`を選択。各項目を入力。

TestPlanにHTTP認証マネージャを追加した場合、すべてのスレッドグループに認証情報が適用される。

|項目|説明|
|:--|:--|
|基底URL|ページを参照するときの基点となるURL<br>エイリアスがあったり、個人の持ち分でサブディレクトリ切ったりしてなければ、だいたいの場合ルートディレクトリのURLでいいっぽい|
|ユーザー名|認証に利用するユーザー名|
|パスワード|認証に利用するパスワード|
|ドメイン|NTLMでの認証のときのドメイン名（らしい）<br>門外なので勉強します。。。|
|Realm|NTLMでの認証のときのRealm名（らしい）|
|Mechanism|BASIC_DIGEST/BASIC/DIGEST/KERBEROSから選択。<br>認証方式については管理者の方に確認確認。|

## 正規表現抽出

たとえば、リクエスト1のレスポンスから値を抽出し、リクエスト2に指定したい場合がある。

追加方法
1. リクエスト1のサンプラーを右クリック。
2. `後処理`->`正規表現抽出`を選択。

|項目|説明|
|:--|:--|
|Apply to|Main sample only: メインのサンプラーの結果のみ対象<br>Sub-samples only: サブのサンプラーの結果のみ対象<br>Main sample Sub-samples: メインとサブの結果両方が対象<br>JMeter Variable: 指定したJMeterの変数の内容が対象|
|Field to check|Body: レスポンスボディが対象<br>Body (unescaped): HTMLエスケープ前のBody部を対象<br>Body as a Document: たとえばWord等のドキュメントを[ApacheTika](http://tika.apache.org/1.2/formats.html)で解析した結果が対象<br>Request Headers: リクエストヘッダが対象<br>Response Headers: レスポンスヘッダが対象</br>URL: URLが対象 <br>Response Code: レスポンスコードが対象（例: 200) <br>Response Message: レスポンスメッセージが対象(例: OK) |
|参照名|抽出した値を格納する変数名<br>値を利用するときもこの変数名を使う。|
|正規表現|値を抽出する正規表現|
|テンプレート|()で抽出した何番目の値を使用するか指定<br>正規表現`(key.*)="(.*)"`の2番目を抽出する場合は`$2$`と指定|
|一致番号|複数マッチングした場合に何番目を抽出するか<br>0または指定なしでランダムらしい（試してない）|
|初期値|マッチするものがなかった場合の値|

3. リクエスト2の`Body Data`の、変数を入れたい箇所に`${'参照名'に指定した値}`と指定すればOK<br>(例) ${data}

## BeanShell
[BeanShell](https://github.com/beanshell/beanshell/wiki)（Javaと同じ文法で書けるスクリプト的な）で処理を記述できる。
複雑なJSONを処理したり、文字列の一括置換なんかがしたいときは`正規表現抽出`より（簡単とは言わないまでも）こちら。

追加方法
1. 処理を挿入したい箇所のサンプラーを右クリック
2. 処理を挿入するのがサンプラー実行前なら`前処理`、実行後なら`後処理`を選択し、`BeanShell xxxxProcessor`を選択
3. スクリプトファイルを指定するか、直接処理を記述。
見りゃわかるけど引数の指定とかもできる。

(例1)
リクエスト1で返ってきたJSONの、パラメータ1個だけ任意の値に一括置換してリクエスト2に指定

1. リクエスト1の`後処理`に以下のBeanShellを追加
項目`weather`のvalueを`sunny`から`rainy`に置換する。
JSON内に`weather`が複数ある場合、すべて置換される。

```java
String responsebody = prev.getResponseDataAsString();

String new_responsebody = responsebody.replace("\"weather\":\"sunny\"","\"weather\":\"rainy\"");

vars.put("responsebody", new_responsebody);
```
2. リクエスト2の`Body Data`には以下を指定

```json
${responsebody}
```

(例2)
特定の箇所で5秒まつ
リクエスト1とリクエスト2の間でちょっと待ちたい場合とか。

```java
Thread.sleep(5000)
```

時間間隔を設定する「タイマー」というエレメントもあるが、追加した階層のすべてに適用されてしまうのでちょっとそぐわない場合もある。