こんにちは、幅広い視野を持つエンジニアを目指しています田中と申します。
今日はApache benchのGUI版であるApache JMeterの応用的な使い方を見ていきます。
RESTクライアントとしてだけではなく、APIのテストツールとしても使います。
はじめに
Apache JMeterとBeanShellを使って、負荷テストをやってみました。
目的
リクエストパラメータに変数を埋め込んでそれを動的に変化させながら負荷テストを行い、レスポンスのバリデーションもしたい
目次
手順の概要
- Apache JMeterのインストール
- スレッドグループの作成と編集
- 起動画面
- スレッドグループの作成
- スレッドグループの編集
- HTTPリクエストの作成と編集
- HTTPリクエストの作成
- リスナーの作成と編集
- リスナーの作成
- Javaでプロトタイピング
- IPアドレスを自動生成する
- BeanShellでコーディング
- リクエストパラメータに変数を埋め込む
- BeanShell PreProcessorの追加
- ロジックを変更
- カスタマイズ(スキップ可能j)
- レスポンスのバリデーションを行う
実行環境
Windows 10 Pro 64bit, Java 1.8.0_144, JMeter 3.2
手順
Apache JMeterのインストール
http://jmeter.apache.org/download_jmeter.cgi
上記のサイトからzipファイルをダウンロードします。
筆者はバージョン3.2をダウンロードしました(Java 8が必要です)。
スレッドグループの作成と編集
起動画面
スレッドグループの作成
まず初めに、スレッドグループを作ります。
スレッドグループは、そのスレッドのAPIテストをどのように行うかの設定になります。
【テスト計画】を右クリックし、【追加】→【Thread(Users)】→【スレッドグループ】を選択します。
スレッドグループの編集
スレッドグループのパラメータの意味は以下です。
【スレッド数】= JMeterが生成するスレッド数、1スレッドが1ユーザに相当する。
【Ramp-up期間】= 何秒かけてそのスレッドを処理するか、0を指定するとすべて同時に実行する。
【ループ回数】= 1つのスレッドが1つのテストケースを何回実行するか。
以上を踏まえるとテストシナリオで重要な項目は以下の計算式で算出できます。
さらにJMeterはスレッドグループにスレーブマシンを使用し並列にテストすることができます。
実際には【テストの総回数】は、
【スレッド数】×【ループ数】× (【リクエストボディの数】) × (【スレーブマシンの数】)のようになることもありますが基本は同じです。
JMeterはループ回数が1の場合、処理開始の時間をスレッドごとに、(Ramp-UP期間[秒] / スレッド数)だけずらして実行してくれるため、
結果として上の画像の場合は(10[秒] / 10) = 1となり、1秒間に1リクエストの計10回のアクセスが行われ、10秒でテストが完了します。
ループ回数を指定せず無限ループのチェックボックスにチェックを入れると無限ループ実行を行います。
(耐久テスト、ある一定のレスポンス速度を保証できるかどうかのテスト向き)
HTTPリクエストの作成と編集
HTTPリクエストの作成
以下のようにして、HTTPリクエストを作成します。
HTTPリクエストはAPIに実際にアクセスする際のHTTPリクエストの設定になります。
【スレッドグループ】を右クリックし、【追加】→【サンプラー】→【HTTPリクエスト】を選択します。
【プロトコル】はWebサーバのプロトコルです。HTTP/HTTPSを指定します。
【メソッド】にはHTTPのリクエストメソッドを指定します、今回はGETとしています。
【サーバ名またはIP】には実際にアクセスするAPIのURL(エンドポイント)を指定します。
【リクエストで送るパラメータ】には任意のパラメータが指定できるのですが、今は何もありません(この後出てきますのでお楽しみに)。
リスナーの作成と編集
リスナーの作成
これで、APIにGETリクエストを送る準備は整いましたが、このままではAPIによって返却されたレスポンスやデータを扱うことができません。
JMeterでは、APIに送った結果を受け取る受け皿の仕組みがあり、結果をグラフとして可視化したりファイルに書き出したりする機能があります。
これを、リスナーといいます。
それでは、リスナーを作ってみましょう。
【HTTPリクエスト】を右クリックし、【追加】→【リスナー】→【結果をツリーで表示】を選択します。
負荷テストでよく使われる統計量を算出するためのリスナーも追加しましょう。
【HTTPリクエスト】を右クリックし、【追加】→【リスナー】→【統計レポート】を選択します。
さて、ここまで設定出来たらウィンドウ上部にある緑のボタンを押してみましょう!
※JMeterを用いてAPIのテストや負荷テストを行う際は必ず自分や所属する組織が作成したWebサーバやAPIに対して事前に許可を取ってから行うようにしてください!
外部のWebサーバやAPIに対し許可無くJMeterでテストを行うことはDoS攻撃とみなされる可能性があります。
左のメニューから【結果をツリーで表示】を選択すると、以下のように出力されています。
APIリクエストに使われたヘッダやリクエストパラメータ、レスポンスコードなどの情報を見ることができます。
左のメニューから【統計レポート】を選択すると、以下のように出力されています。
この統計レポートの見方は以下になります。
ヘッダ | 意味 |
---|---|
Label | サンプラーの名称 |
#Samples | サンプル数 |
Average | 平均応答時間(ms) |
Median | 応答時間の中央値(ms) |
90% Line | 90%信頼区間(少なくとも90%はこの値に収まる。単位ms) |
95% Line | 95%信頼区間(少なくとも95%はこの値に収まる。単位ms) |
99% Line | 99%信頼区間(少なくとも99%はこの値に収まる。単位ms) |
Min | 最小応答時間(ms) |
Max | 最大応答時間(ms) |
Error% | エラーの割合(%) |
Throughput | スループット(1秒間に何件のリクエストに対し応答できたか) |
KB/sec | 1秒当たりの平均転送データ量(KB) |
ここまでで、JMeterをRESTクライアントおよび負荷テストの指標として使うことができました!
ここからは、IPアドレスを受け取ると経度および緯度情報を返すAPIに対し、JMeterを使用して有効なIPアドレスを生成し、
返ってきたレスポンスが正しいか検証するAPIテストクライアントとしてのJMeterシナリオを作成していきましょう!
Javaでプロトタイピング(スキップ可能)
IPアドレスを自動生成する
以前の私の記事のネットワークアドレスの秘密を参考に、有効なIPアドレスを生成するコードをJavaで書いてみました。
IPアドレスを整数((32bit unsigned int)0 ~ 4,294,967,295)に変換することで、複雑な正規表現チェックをしなくて済むという内容でした。
JMeterはBeanShellというJava処理系のコードを実行できるので、こちらのコードを編集して、BeanShellのコードにしていくことを目指します。
※コードが拙いのは愛嬌ということで・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import java.io.IOException; class DetailOfIpAddress { public static String uint2ipaddr(String uintAsString) { String ipAddressAsString = ""; try{ int octet_binary_0 = (Integer.parseUnsignedInt(uintAsString) >> 24) & 0xff; int octet_binary_1 = (Integer.parseUnsignedInt(uintAsString) >> 16) & 0xff; int octet_binary_2 = (Integer.parseUnsignedInt(uintAsString) >> 8) & 0xff; int octet_binary_3 = Integer.parseUnsignedInt(uintAsString) & 0xff; StringBuilder sb = new StringBuilder(); sb.append(octet_binary_0); sb.append("."); sb.append(octet_binary_1); sb.append("."); sb.append(octet_binary_2); sb.append("."); sb.append(octet_binary_3); ipAddressAsString = sb.toString(); } catch(NumberFormatException e) { System.out.println("有効な符号なし整数ではありません!"); System.out.println(e); } return ipAddressAsString; } public static void main(String args[]) throws IOException { String ipAddressAsString; if(args.length != 1) { System.out.println("指定できる引数は1つです!"); return; } ipAddressAsString = uint2ipaddr(args[0]); System.out.printf(ipAddressAsString); } } |
BeanShellでコーディング
リクエストパラメータに変数を埋め込む
以下のように、HTTPリクエストを編集します。
左のメニューから【HTTPリクエスト】を選択します。
画面下部の【追加】ボタンから【リクエストで送るパラメータ】にパラメータ名:ip、値:${RANDOM_IPADDR}を追加。
BeanShell PreProcessorの追加
BeanShellのスクリプトを実行するために、BeanShell PreProcessorを追加しましょう。
【HTTPリクエスト】を右クリックし、【追加】→【前処理】→【BeanShell PreProcessor】を選択します。
ロジックを変更
Javaのプロトタイプではコマンドライン引数として32bit符号なし整数を受け取っていました。
BeanShellでもコマンドライン引数を受け取れますが、今回はスレッド毎に乱数を生成するようにします。
BeanShellについてはこちらをご覧ください。
BeanShellではJDK1.5.0互換のJavaの文法とAPIがそのまま使えます。(ちょっと古いなあ…)
Script:を以下のように編集しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
random = String.valueOf((1L >> 31) + (int)(Math.random() * Integer.MAX_VALUE) + (int)(Math.random() * Integer.MAX_VALUE)); // ((32bit unsigned int)0 ~ 4,294,967,295)の乱数を生成 /* IPアドレスの各オクテットを生成 */ int octet_binary_0 = (Integer.parseUnsignedInt(random) >> 24) & 0xff; // IPアドレスの第一オクテット int octet_binary_1 = (Integer.parseUnsignedInt(random) >> 16) & 0xff; // IPアドレスの第二オクテット int octet_binary_2 = (Integer.parseUnsignedInt(random) >> 8) & 0xff; // IPアドレスの第三オクテット int octet_binary_3 = Integer.parseUnsignedInt(random) & 0xff; // IPアドレスの第四オクテット /* IPアドレスの各オクテットからIPアドレスを生成 */ StringBuilder sb = new StringBuilder(); sb.append(octet_binary_0); sb.append("."); sb.append(octet_binary_1); sb.append("."); sb.append(octet_binary_2); sb.append("."); sb.append(octet_binary_3); ipAddressAsString = sb.toString(); vars.put("RANDOM_IPADDR", String.valueOf(ipAddressAsString)); // RANDOM_IPADDRに生成されたIPアドレスを代入 |
vars.put("変数名", "変数の値");でクエリパラメータの${変数名}に値を渡すことができます!
JMeterのインスタンス変数のctx, vars, props, prev, sampler, logにアクセスしたりJMeterのAPIを呼ぶこともできます。
詳しくはこちらをご覧ください。
ここまで書けたらボタンを押して実行してみましょう!
URLを見るとちゃんと有効なIPアドレスのパラメータを追加してアクセスできていますね!
カスタマイズ(スキップ可能)
さてBeanShellはJavaのプログラムなのでJavaでできることは基本的に何でもできます。
ここでは、JMeterのAPIにアクセスしてスレッドの情報をデバッグ情報として表示したり動作ログを出力してみましょう。
JMeterをJMeterがインストールされているフォルダのApacheJMeter.jarではなく、jmeter.batを起動するか、下記コマンドを実行します。
1 |
jmeter -n -t [jmxファイルのパス] -l [結果ファイルのパス] -e -o [レポートの出力先フォルダ] |
JMeterをCLIモードとして起動することができます。
jmeter.batの場合、GUIとコンソール出力を受け取るためのコマンドプロンプトが両方起動します。この先はjmeter.batを使用した場合を想定します。
例えば、以下のようなコードを追加できるか試しましょう。
1 2 3 4 5 6 7 8 9 10 |
System.out.println("サンプラー名: " + sampler.getName()); // サンプラー名を取得 System.out.println("スレッド名: " + ctx.getThread().getThreadName()); // スレッド名を取得 /* パラメータの数だけループ */ for (int i=0;i<sampler.getArguments().getArgumentCount();i++) { System.out.println("パラメータ: " + sampler.getArguments().getArgument(i)); // i番目のパラメータを表示 } System.out.println("アクセス先URL: " + sampler.getUrl()); // アクセス先URLを表示 // 何かやりたいこと System.out.println(""); // 改行を出力 |
動作状況を出力することができました!(概ね1秒ごとにリクエストできていることも見て取れます。)
なお、log.info("文字列")などとすることで、JMeterの動作ログファイルに文字列を書き込むこともできます。
レスポンスのバリデーションを行う
JMeterはデフォルトでHTTPレスポンスコードの"200 OK"が返されるとリクエスト成功と判定しますが、APIテストではさらにレスポンスに対してバリデーションをかけたいこともあります。
次はレスポンスをBeanShellでバリデートしてみましょう!
【HTTPリクエスト】を右クリックし、【追加】→【アサーション】→【BeanShellアサーション】を選択します。
さらに【HTTPリクエスト】を右クリックし、【追加】→【リスナー】→【アサーション結果】を選択します。
今回作成したAPIはパラメータとしてIPアドレスを受け取り以下のようなレスポンスを返すと想定します。
1234567891011121314151617181920 {"items": [{"found": true,"info": {"city": "Mountain View","continent": "North America","country": "United States","location": [37.419200000000004,-122.0574],"postal_code": "94043","subdivision": "California","time_zone": "America/Los_Angeles"},"ip_address": "144.189.123.1"}]}
IPアドレスによっては国名が取得できなかったりするのでそれを検知したいとします。
例えば、以下レスポンスとなります。
12345678 {"items": [{"found": false,"ip_address": "193.53.1.45"}]}
スクリプトに以下コードを記述します。
1 2 3 4 5 6 |
String result = new String(ResponseData); // レスポンスを受け取る if (!(result.contains("country"))) { Failure = true; // 失敗とする FailureMessage = "Request failure: " + result; // アサーション結果に失敗理由を出力する } |
もちろんSystem.out.println()等を実行してコンソールにデバッグ情報を出力することも可能ですが、自動化との親和性が高いであろう、ログファイルを指定する方法を採用します。
ボタンを押して実行しましょう!
すると・・・
国名を取得できないIPが見つかりました!
アサーション結果も見てみましょう。
193.53.1.45はどこの国のIPアドレスか不明ということですね…(ちなみにデータベースはGeoIP2を使用してます)
GeoIP2のデモサイトでも見つからないと出てますね。
まとめ
Apache JMeterを使うことで高速かつ低コストに負荷テストおよびAPIテストが実現できる!!
ここまで読んでくださりありがとうございました!
・引用元および参考にさせていただいたサイト様
投稿者プロフィール
最新の投稿
- AWS2021年12月2日AWS Graviton3 プロセッサを搭載した EC2 C7g インスタンスが発表されました。
- セキュリティ2021年7月14日ゼロデイ攻撃とは
- セキュリティ2021年7月14日マルウェアとは
- WAF2021年7月13日クロスサイトスクリプティングとは?