JMeterを利用したGraphQLの負荷テスト

はじめに

こんにちは。株式会社ビデオマーケットでサーバサイドエンジニアを担当しております、yamakzと申します。

弊社では一部のサービスでAPIにGraphQLを利用しております。 GraphQLはデータを複数のデータソースから取得するリクエストを 1 つのAPI 呼び出しで構成できます。 さらに、API を保守する際、既存のクエリに影響を与えることなく、フィールドの追加や廃止を柔軟に行えます。 RESTとは違いエンドポイントが単一のため、そういった意味では負荷テストも容易です。 しかし、RESTより情報が少なく実行して気づいた点もありますので、今回記載いたします。

JMeter について

Apache JMeterはクライアントサーバシステムのパフォーマンス測定および負荷テストが可能なJavaアプリケーションです。 HTTPレスポンスの内容の妥当性を判定することもできるため、パフォーマンステストだけではなく、機能テストに使用することも可能です。 負荷テストツールでは有名なものの一つだと思います。

今回こちらを利用した理由はGraphQLのリクエスト作成が容易だった為です。 GraphQLのリクエストはcurlで書くと下記のようになります。

$ curl -H 'Content-Type: application/json' -H "Authorization: token" -X POST -d " \
 { \
   \"query\":\"query{\n  notices(page: 1){\n    headline\n    description\n  }\n}\" \
 } \
" http://localhost/graphql

内部のクォートはエスケープしないとクエリがパースできないので、素で書くとその辺りがややこしいです。 サンプル程度の短いクエリであれば、問題がありません。しかし要求するデータ量が多くなればなるほど、フィールドを指定する都合上、クエリも長文になります。 従って、エスケープを考慮せずにクライアントツールで動作できる状態のクエリをそのまま利用可能なJMeterを選択しました。

JMeterの導入

Homebrewを使用する場合

Macの場合はHomebrewを使用するのが簡単です。 Homebrewは既に導入されている前提ですと、下記二つのコマンドだけでインストールと実行が可能です。

  • インストール
$ brew install jmeter
  • 起動
$ jmeter
  • 実行ファイルの修正
    • ただし、このままだとファイルを開いたり保存したりする場合、例外が発生し、ダイアログが表示されないことがあります。
    • その場合、jmeter の実行ファイルに下記を追記します。
    • 私の環境の場合、以下のファイルでした。
      • /usr/local/Cellar/jmeter/5.4.1/libexec/bin/jmeter

このファイルに JAVA9_OPTS=.... —add-opens=xxx という形で列挙されている部分があります。 その末尾に --add-opens=java.desktop/sun.awt.shell=ALL-UNNAMED を追加します。

ダウンロードの場合

Apache JMeterからダウンロードします。

http://jmeter.apache.org/download_jmeter.cgi

テストの作成

ここからは最低限設定すべき事項について記載します。

  1. 起動するとこの画面になるので、「Test Plan」を右クリックして下記のようのメニューを進み「Thread Group」を押します。 f:id:yamakz:20210917110107p:plain
  2. このような画面が表示されるので、中央にある下記項目を設定します。
    • Number of threads (同時に何ユーザーが接続するか?)
    • Ramp-up period(何秒の間に上記ユーザーが接続するか?)
    • Loop Count(何回繰り返すか?)

f:id:yamakz:20210917110232p:plain 3.「Thread Group」を右クリックして下記のようにメニューを進み「HTTP Header Manager」を開きます。 f:id:yamakz:20210917110305p:plain 4. アクセスに必要なヘッダー情報を設定します。 - この時「Content-type:application/json」を設定し忘れるとサーバー側で400エラーになる場合があるので注意です。

f:id:yamakz:20210917110301p:plain 5.「Thread Group」を右クリックして下記のようにメニューを進み「GraphQL HTTP Request」を開きます。 f:id:yamakz:20210917110257p:plain 6. 下記情報を設定します。

  • ホスト及びポート
  • パス
  • GraphQLのクエリ(変数は別タブに分けることもできます)

f:id:yamakz:20210917110253p:plain 7. こちらは任意ですが下記のように「View Result Tree」を開いておくと、レスポンスが閲覧できます。 f:id:yamakz:20210917110249p:plain 8. 右上のフロッピーディスクのようなボタンから保存します。
9. 再生ボタンからも実行できますが重いので、負荷テストの実行はCUIで実行することが推奨されています。

下記のように実行したいテストのjmxファイルとログを出力するファイルを指定します。

$ jmeter -n -t my_test.jmx -l log.jtl

実行すると下記のようにsummaryが表示されます。

  • summary:実行回数
  • Avg:平均応答時間(ms)
  • Min:最小応答時間(ms)
  • Max:最大応答時間(ms)
  • Err:エラー数と割合
  • Active:実行中リクエスト
  • Started:開始リクエスト
  • Finished:終了リクエスト
Creating summariser <summary>
Created the tree successfully using NoticeTestPlan.jmx
Starting standalone test @ Fri Sep 24 09:48:54 JST 2021 (1632444534537)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary +    404 in 00:00:05 =   74.9/s Avg:  1054 Min:   173 Max:  1951 Err:     0 (0.00%) Active: 100 Started: 100 Finished: 0
summary +   2596 in 00:00:29 =   90.5/s Avg:   931 Min:    41 Max:  2648 Err:     0 (0.00%) Active: 0 Started: 100 Finished: 100
summary =   3000 in 00:00:34 =   88.0/s Avg:   948 Min:    41 Max:  2648 Err:     0 (0.00%)
Tidying up ...    @ Fri Sep 24 09:49:28 JST 2021 (1632444568705)
... end of run

ログには詳細な各リクエストの結果が記載されます。 各値の内容はヘッダーに記載されている通りです。

timeStamp,elapsed,label,responseCode,responseMessage,threadName,dataType,success,failureMessage,bytes,sentBytes,grpThreads,allThreads,URL,Latency,IdleTime,Connect
1632444534891,1284,GraphQL HTTP Request,200,OK,Thread Group 1-6,text,true,,1213,1380,100,100,http://localhost/graphql,1280,0,128
1632444534889,1327,GraphQL HTTP Request,200,OK,Thread Group 1-11,text,true,,1213,1380,100,100,http://localhost/graphql,1327,0,200
...

終わりに

GraphQLはRESTより情報量が少ないので、苦労する部分もあります。 しかし、エンドポイントが一定なので、クエリを変更してあげるだけでテストが行えるのはメリットだと感じました。 GraphQLを使用する方が少しでも増え、その方達の助けになれば幸いです。

参考

公式のドキュメントの他、以下も参考にいたしました。

https://stackoverflow.com/questions/67615212/why-am-i-not-able-to-click-on-open-icon-in-jmeter