(いまさら)JMeter入門

## イントロ

お仕事でWEB APIの性能評価をする機会があり、それをやった際の備忘メモ。
遠い昔(ほんとに15年くらい前)にJMeterを使って性能評価をときどきやっていましたが、今ググってもまだ現役のようなので、今回もJMeterをつかいました。

てことであらためて再入門として、セットアップからHelloworld的なところまで整理しておこうと思います。



## テスト対象サーバとか、環境の整理とか

環境の整理ですが、JMeterはコマンドプロンプトから使いますのでWindowsにJavaを入れておきましょう。

```console
C:\Windows\System32>java --version
java 17.0.11 2024-04-16 LTS
Java(TM) SE Runtime Environment (build 17.0.11+7-LTS-207)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.11+7-LTS-207, mixed mode, sharing)

C:\Windows\System32>
```

またテスト対象のサーバですが、誰かのサイトに負荷をかけるわけにはもちろんいかないので、

https://qiita.com/masatomix/items/53003a34d413206bb619

の記事にでてくるBackendサーバ(RESTサーバ)をローカルにたてることにします。


サーバのセットアップ手順だけざっと記載すると(記事通りWSLでやってます。WSLにもJavaを入れておきましょう)

```bash
$ java --version
java 17.0.8 2023-07-18 LTS
Java(TM) SE Runtime Environment (build 17.0.8+9-LTS-211)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.8+9-LTS-211, mixed mode, sharing)
$
```

```bash
$ git clone -b init https://github.com/masatomix/spring-data-rest-example.git
$ cd spring-data-rest-example/
$ ./gradlew clean bootRun
```

これだけです。APIはたとえば  

```bash
http://[サーバ名]:8080/companies?page=0&size=20
http://[サーバ名]:8080/users?page=0&size=20
```

とかが動くようになったと思います。

また、あとでプロキシを経由して接続する都合上、 localhostではなくIPアドレスでアクセスをしたいので、WSLの方で下記のようにIPを調べておきましょう。

```bash
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet 10.255.255.254/32 brd 10.255.255.254 scope global lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:15:5d:7a:26:0a brd ff:ff:ff:ff:ff:ff
    inet 192.168.127.67/20 brd 192.168.127.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:fe7a:260a/64 scope link
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:5f:cd:00:30 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
$
```

`` 192.168.127.67 `` をメモっておきます。


## JMeterのインストールとセットアップ

https://jmeter.apache.org/download_jmeter.cgi よりダウンロード。2024/12/04時点の直近版は

https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.3.tgz

のようですね。ダウンロードして、適当なところに展開しておきましょう。

さて起動します。

```console
C:\Windows\System32>cd c:\javatools\apache-jmeter-5.6.3\bin\

c:\javatools\apache-jmeter-5.6.3\bin>jmeter.bat
================================================================================
Don't use GUI mode for load testing !, only for Test creation and Test debugging.
For load testing, use CLI Mode (was NON GUI):
   jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
& increase Java Heap to meet your test requirements:
   Modify current env variable HEAP="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" in the jmeter batch file
Check : https://jmeter.apache.org/usermanual/best-practices.html
================================================================================
```

![000](https://imgur.com/AB0Om9F.png)

起動したようですね！

まずは使いやすいように日本語に切り替えておきましょう。
Options > Choose Language > Japaneseを選択します。 


![001](https://imgur.com/KQiR3yh.png)

日本語に切り替わったようですね。
![002](https://imgur.com/wWOo0N1.png)



## とりあえずやってみる

ざっと必要な設定手順を書いていきます。これだけみてるとなんのこっちゃかもしれませんが、とりあえずやってみましょう。

### 負荷度合いやループ回数の設定

```
Test Plan > 追加 > Threads > スレッドグループ
```

![003](https://imgur.com/gvg2IIx.png)
下記の設定が追加されました。

![004](https://imgur.com/TZxGU92.png)

スレッドグループの設定では、

- スレッド数
- 何秒かけてそのスレッド数に到達するか
- ループ回数(上の設定を何回ループするか)

などを設定します。たとえば

- スレッド数 5
- Ramp-Up期間 10秒
- ループ回数 1

とした場合、10秒で5スレッドを起動するので、2秒に1回(初回の)クエリが発行されることになります。徐々に負荷を高めていって、最終的なスレッド数に到達するイメージですね。


### 接続先情報を設定

つづいて接続先のサーバを指定します。

```
Test Plan > 追加 > 設定エレメント > HTTPリクエスト初期値設定
```

![005](https://imgur.com/FzfUbtC.png)

下記の設定が追加されました。

![006](https://imgur.com/XYLkpwx.png)

この画面は、リクエストの共通設定をする箇所です。よく使うのは「プロトコル」「サーバ名」「ポート番号」ですね。今回は

- プロトコル: http
- サーバ名: 192.168.127.67
- ポート番号: 8080

としておきました。

### 個々のリクエストを設定する

具体的なリクエスト(サンプラー)を追加します。冒頭で書いた

```
http://[サーバ名]:8080/companies?page=0&size=20
http://[サーバ名]:8080/users?page=0&size=20
```

を追加してみます。

```
スレッドグループ > 追加 > サンプラー > HTTPリクエスト
```

![007](https://imgur.com/WoP7GAL.png)

下記のような設定が追加されました。

![008](https://imgur.com/N9wLM9E.png)

なんとなくわかるかも知れませんが、HTTPのメソッドや``/companies``などのリクエストパス、リクエストパラメータ(``page=0``とかね)を設定する事ができます。共通部分のサーバ名などは「 HTTPリクエスト初期値設定」で設定済みなので省略可能です。

同様に ``/users``分も追加しておきましょう。
また名前について、判別できるように  ``_companies`` とか、``_users`` とかをつけておきました。

![009](https://imgur.com/qSAvGwu.png)

いったんCtrl-sで好きな場所に保存しておきましょう。

###  結果を表示するリスナーを設定

JMeterで負荷をかけたときに、リクエストが正しく実行できているかとか、平均レスポンスはどれくらいかな？とかを表示したいですよね。

それらを表示する「リスナー」を追加します。

```
Test Plan > 追加 > リスナー > 結果をツリーで表示
```

![010](https://imgur.com/7Al20i0.png)

追加されました。
下記はテスト実行後の表示なのですが、リクエスト・レスポンスの結果など、リクエストごとの詳細結果を表示することができます。


![011](https://imgur.com/zKpBJMt.png)

つづいて統計レポート。

```
Test Plan > 追加 > リスナー > 統計レポート
```


![012](https://imgur.com/i3WRteq.png)

追加されました。
このようにリクエストごとの(名前がキーで集計されてそう)

- 実行件数
- 平均、最大、最小、メジアンのレスポンスタイム
- 90/95/99パーセンタイルのレスポンスタイム
- スループット(件/s)
- エラー率

などを表示できます。

![013](https://imgur.com/ibFP7Xa.png)


またCtrl-sで保存しておきましょう。

### 実行してみる

ようやく実行です。実行はボタン上部の再生マークで。

![014](https://imgur.com/sg47bF8.png)

スレッドグループは

- スレッド数 5
- Ramp-Up期間 10秒
- ループ回数 1

の設定だったので、2つのAPIに2秒に一回リクエストが飛びます。合計10リクエスト。

サーバ側のログを見たり、さきほどのリスナー(ツリー表示や、統計レポートのこと)をみていると、リアルタイムに状況が分かると思います。実行結果は既出のキャプチャの通りです。

おつかれさまでした。

## まとめ

実際に性能評価する場合は、

- 10分くらいずっと負荷をかける(スレッドグループの設定で、ループ回数を無限、持続時間で10分を指定)
- 一様乱数タイマーを使ってリクエスト間隔をばらけさせることで、リクエストが周期的に集中しないようにする
- APIごとの利用度合いによってリクエスト負荷を調整したいので、スレッドグループを複数作って負荷を調節する。
- GUIで実行するのではなく、CUI(コマンドライン)からJMeterを実行して、計測結果をファイル出力にしてグラフィカルに集計
- 実はエラーでコケてたりしないよう、リクエストごとの実行結果が正常かをアサーションする
- 手動でチマチマシナリオ(今回作ったヤツ)を作るのは大変なので、プロキシ機能でリクエストをキャプチャしてシナリオを作る

などなどのナレッジがあったりしますが、まずはHelloWorldレベルは完了です。お疲れさまでした。



## TIPS

### プロキシ機能でキャプチャ

さきほどリクエスト(サンプラー)を設定しましたが、一つ一つ手動で設定していくのはとても面倒なので、JMeterの電文キャプチャ機能を使ってみます。ながれとしては

- ブラウザでプロキシ設定する(JMeter経由のリクエストにする)
- ブラウザでWEB操作(今回はSwaggerがあるのでそれを使います)
- そのリクエスト情報がJMeterに記録される

こんな感じです。

#### JMeter側の設定

まずはJMeterに設定を追加していきます。

```
Test Plan > 追加 > Non-Testエレメント > HTTPプロキシサーバ
```

![015](https://imgur.com/hc0onTf.png)

追加されました。
![016](https://imgur.com/BLmBbXF.png)



ポートが8888であることを確認しつつ、あとで戻ってくるのでいったん次にいきます。



```
スレッドグループ > 追加 > ロジックコントローラ > 記録コントローラ
```

![017](https://imgur.com/CpjaXOn.png)
リクエストを記録する用のコントローラが追加されました。[^1]

[^1]: 単にクリアボタンがあるコントローラと思われる

![018](https://imgur.com/UsZqBO8.png)

再度プロキシサーバの設定に戻って、記録先を「記録コントローラ」にしておきます。

![019](https://imgur.com/2BKVWIX.png)

開始をクリックすると、

![020](https://imgur.com/zhCDkTM.png)

警告みたいなのがでますが、このままOKします。

![021](https://imgur.com/RDfgIqW.png)

プロキシ機能が起動したようです。

![022](https://imgur.com/MOPp8ky.png)



#### ブラウザ側の設定

ブラウザ側をJMeter経由でリクエストするための設定をします。といってもよくあるプロキシ設定です。
Chromeとかだと下記のインターネットのプロパティから。JMeterのプロキシをポート8888であげておくので

- アドレス: http://localhost
- ポート: 8888

としました。



![023](https://imgur.com/gmQBxVe.png)

:::note warn
設定した時点でOSのリクエストがJMeter経由になっちゃうので、実際は下記のリクエストを投げる直前にOFF/ONした方がいいです。
:::



さてChromeを起動して、おなじみのSwagger画面( http://192.168.127.67:8080/swagger-ui/index.html )へ。

![024](https://imgur.com/ABPOk3T.png)

Executeボタン等で実行してみると、、、

![025](https://imgur.com/DpWDDnS.png)

おお、リクエストがキャプチャされましたね！

詳細もみてみると、さきほどは手動で設定したリクエストが自動でセットされていることが分かります。

![026](https://imgur.com/Y4pz8Qj.png)

リクエストヘッダもちゃんとキャプチャしてくれていますね。手動じゃめんどくてできない、、。

![027](https://imgur.com/zTYxxz8.png)

キャプチャが終わったら停止をクリックしてキャプチャ機能を停止しておきましょう。

![028](https://imgur.com/8VvmjHL.png)

ブラウザのプロキシ設定を戻すのも忘れずに。

![029](https://imgur.com/ofCuNVD.png)

おつかれさまでした。

### GUIではなく、コマンドプロンプトから実行する

GUIで起動すると実は、

```console  
Don't use GUI mode for load testing !, only for Test creation and Test debugging.
For load testing, use CLI Mode (was NON GUI):
   jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
& increase Java Heap to meet your test requirements:
   Modify current env variable HEAP="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" in the jmeter batch file
Check : https://jmeter.apache.org/usermanual/best-practices.html
```

って 「GUIでは負荷テストするなよ！テスト作成とデバッグの時だけね」って書いてあるので、指示通りCLI(GUIじゃなくてプロンプトとか) から実行しようと思います。

その際のパラメタも書いてあるので指示通りにやってみると、、。

```console
C:\Users\xxx\Desktop\result> c:\javatools\apache-jmeter-5.6.3\bin\jmeter.bat -n -t "..\sample.jmx" -l result.jtl -e -o output
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
WARN StatusConsoleListener The use of package scanning to locate plugins is deprecated and will be removed in a future release
Creating summariser <summary>
Created the tree successfully using ..\sample.jmx
Starting standalone test @ 2024 Dec 12 14:36:49 JST (1733981809986)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary =     10 in 00:00:08 =    1.2/s Avg:    20 Min:     8 Max:   101 Err:     0 (0.00%)
Tidying up ...    @ 2024 Dec 12 14:36:58 JST (1733981818226)
... end of run

C:\Users\xxx\Desktop\result>
```

10秒ほどするとテストが完了しました。上記にサマリが書いてありますが、10件実行されエラーは0件なので、どうやらOKそうですね。

また、指定した箇所に jtlファイル(実行結果の素データ) とHTMLファイルが出力されています。そのHTMLを開いてみると、、

例: 統計レポートぽいやつ

![030](https://imgur.com/Aigy9v5.png)

例: パーセンタイルごとのレスポンスタイム

![031](https://imgur.com/nozrnRf.png)

などなど、実行結果の統計情報がHTMLでグラフィカルに出力されていました。



## 関連リンク


- https://jmeter.apache.org/ 公式
- https://www.masatom.in/pukiwiki/JMeter/ 15年以上まえに整理した記事。しかも途中で力尽きている
- https://christina04.hatenablog.com/entry/2017/10/03/190000
- https://ptune.jp/tech/scenario-creation-method-using-http-proxy-server/ めっちゃ丁寧にJMeterを解説しています。