JMeter-WebSocketSamplerを使用したSocket.IOの負荷試験




どうも高妻です。Twitterでもプログラミングのことや暗号通貨のことをつぶやいているのでよかったら覗きにきてください。  

Socket.IOの負荷試験どうやってますか?

今回はSocket.IOサーバーの負荷試験について書きたいと思います。

負荷試験でよく使われるツールとしてJMeterがありますが、JMeterはこのままではwebsocketに対応していないのでプラグインを追加する必要があります。

追加するプラグインや設定等に関してはmaciejzaleski/JMeter-WebSocketSamplerを参考にしていただければと思います。

GitHubに下記で紹介するJMeterのシナリオがあるのでこっちも参考にしてください。ちなみにリアルタイム通信をすぐ始められるOSSです。

/Real-Time-Server-Side 

以降、JMeterに導入した前提でWebSocketSamplerの使い方だけ説明したいと思います。

手順1

socket.ioを起動しているIPを設定し、Pathにsocket.io/?EIO=3&transport=websocketを設定します。

Pathに設定しているものはsocket.ioの仕様で決まっており、EIOはengine.io-protocolのバージョン3を使用して、トランスポートにwebsocketを使用するという設定になります。pollingからwebsocketにUpgradeして接続したい場合はtransport=pollingと設定すれば接続できます。

Streaming connectionにチェックを入れておくと他のwebsocket samplerからでも同じコネクションを使用することができます。その時の同じコネクションかどうか判定するものにConnection Idを使用します。

参考画像ではUSER_IDを渡すことで同じユーザーは同じコネクションを使用できるように設定しています。

手順2

接続時のRequest dataではsocket.ioのname spaceを指定することができます。

40/というデータの意味は先頭の4がengine.ioプロトコルではmessage packet type,2番目の0がsocket.ioプロトコルではCONNECT type、最後の/は接続するnamespaceを表しています。

例えば/gvgというnamespaceに接続する場合はここのデータを40/gvgとすれば/gvgに接続することができます。接続が完了するとサーバーから40というメッセージが返ってきます。/は省略できるので40/と同じ意味となります。

サーバーから返ってくるデータの40をResponse patterに指定することでこのwebsocket samplerでの処理を終了することができます。

また、Close connection patterに1.*(1でも可)と指定するとサーバーから1(engine.ioプロトコルでclose packet typeの意味)が返ってきたときに接続を閉じることができます。

Message backlogはサーバーから受け取ったデータを何個まで保持するかの設定になります。

手順3

接続が完了したらサーバー側のイベントを呼ぶことができるようになります。下記画像はroom_joinというイベントに{roomId:1}というデータを送るときの設定です。

先頭の4はengine.ioプロトコルでmessage packet type、2はsocket.ioプロトコルでEVENT type、/gvgはnamespace、配列[ ]内の1番目がイベント名、2番目が送るデータになります。

手順4

サーバーからデータを受け取る場合は少し工夫が必要になります。

HTTPの場合はリクエストとレスポンスが一対一で対応するので一つのサンプラーでリクエストとレスポンスの処理が完了しますが、websocketの場合はそうではないのでデータを送るサンプラーと受け取るサンプラーを別々に分けます。

データを送るサンプラーは上記手順の3番になります。

データを受け取るときに問題になるのがwebsocketの場合はサーバーの任意のタイミングでデータが送られてくるのでそれまでwebsocketが切断されないようにする必要があります。その設定が下記画像の設定になります。

Request dataの2はengine.ioプロトコルでping packet type、Response patternの3はpong packet typeという意味になります。このデータでクライアントは定期的にサーバーにハートビートを送り、接続が切られないようにできます。

手順5

次に手順4のハートビートを繰り返す処理を設定します。

手順4のwebsocket sampler(room waiting)の上にwhile controller(room待機中)を追加し、ConditionにisLoopという変数を使用します。

isLoopはこの後の処理で値が変更され、falseになるとwhile controllerを抜けるようにします。ここでisLoopがfalseになるのは他のユーザーがroomに入ってきて人数が一定数に達したら場合を想定しています。

手順6

room waitingの子要素にJSR223 Listenerを追加します。

JSR223 Listenerはサーバーから送られてくるデータを受け取ったあとに処理を加えることができるリスナーです。言語設定と実際の処理を記述することができます。

画像ではJavascriptで記述し、roomに入った人数が2人を超えたらisLoopがfalseになるような処理を書いています。

手順7

実際にサーバーから送られてきているデータは下記です。

3はハートビートを送った応答としてサーバーから返ってきている値です。
他のユーザーがroomに入るとそのroomに所属しているクライアントのsocketIdの配列を通知するようにサーバーで実装すると[Message 36]のようなデータが送られてきます。

このデータを上記6のスクリプトで処理するとisLoopの値を変更できます。

まとめ

以上がJMeter-WebSocketSamplerを使用したSocket.IOへの負荷のかけ方になります。

webosocketはhttpと違って負荷のかけ方に工夫が必要になるということが分かったと思いますが、基本的には上記手順の組み合わせでいろんなパターンで負荷をかけることができると思います。

自作スクリプトでもいいですが、Node.jsの書き方が分からなくても負荷をかけられるようになるので是非試して見てください。

上記JMeterのシナリオをGitHubで公開しているので是非参考にしてみてください。

/Real-Time-Server-Side 

最後に下記リンクもSocket.IOとNode.jsの理解が深まる内容になっているので是非読んでみてください!!







2 件のコメント

  • 情報ありがとうございました。大変参考になりました。
    ちなみMaster-Slave構成で組んだ場合には、下記エラーがでました。
    Error in rconfigure() method java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
    java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
    java.lang.ClassNotFoundException: JMeter.plugins.functional.samplers.websocket.WebSocketSampler (no security manager: RMI class loader disabled)

    Masterのjmeter.propertiesファイルで「mode=standard」にすると正常にMaster-Slave構成で動作しました。
    ご参考まで。

    • コメントありがとうございます。少しでも参考になったみたいでよかったです^ ^
      Master-Slave構成のところが分からないのですが、何をMaster-Slave構成にしたのでしょうか?

  • コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です