JMeter を使った ActionCable ( WebSocket ) 負荷テストの設定方法

最近、Railsアプリケーション開発でもReact.jsやVue.jsなどを使ったフロント開発の案件が増えてきました。 比較的規模が大きなシステムでは

  • リリース前にどの程度の高負荷まで耐えられるか
  • リリース後でもステージング環境でどの程度の高負荷まで耐えられるか

を事前に知りたい(もしくは、POやお客さんから依頼がある)ケースが少しずつ出てくるかと思います。

JMeterを使ったActionCable の負荷テストの設定を行ったので、設定内容とその背景を書いていきます。

JMeterを使ってActionCableのテストを作るメリット

ActionCableの負荷テストをする方法は色々ありますが、Rubyスクリプトを書いたり、ruby-jmeterやgatling他の負荷テストツールを使う場合と比較し、以下の観点で総合的にメリットが大きいと感じたため、JMeterを使うことにしました。

  • 古くからあって安定している(遅いけど)
    • => 途中で落ちたりしにくいので、うまく動かずはまるリスクが低い
  • 情報量が多く、設定がしやすい
    • => どんな挙動をするのか事前に想定を置いてテストを書きやすい
  • そこそこ複雑なシナリオをテストできる
    • => 適用できるシステムが多く、設定ファイルを他の案件にも再利用しやすい
  • jmeter-serverを使うことで、複数端末からの攻撃にスケールが容易である
    • => 実運用の想定に近い負荷テストの実行が可能
  • GUIでグラフが表示できる
    • => 早くテスト結果を把握できることで、チューニングとテストの再実行が容易になる

環境

  • Rails 5.2.3
  • JMeter 5.1.3 ( 未インストールの場合はこちらの記事を参照 )

JMeterJMeter WebSocket Samplersをインストール

以下の記事を参考にインストールしてみてください。 simple-minds-think-alike.hatenablog.com

ちなみに今回使用したJMeter Pluginは「WebSocket Samplers by Peter Doornbosch version 1.2.1」です。

Rails側の設定

既にActionCableを使ったアプリケーションがある想定で、JMeterの負荷テストを実施するために追加が必要な設定(リクエストオリジンの設定)だけを書いておきます。

リクエストオリジンの設定

以下のようにリクエストオリジンを設定することによってCSRF対策を回避しました。

まずは、ローカルでの動きをみたいのでconfig/environments/development.rbに記載します。

config.action_cable.allowed_request_origins = ['https://load-testing.com']

補足

他の箇所としては、以下のように

  • パス/cable がマウントされている状態
  • HomeChannel#speakを呼び出す

というRails側実装を想定してテストを作りました。

config/routes.rb

Rails.application.routes.draw do
  # 〜〜〜
  mount ActionCable.server => '/cable'
end

app/channels/home_channel.rb

class HomeChannel < ApplicationCable::Channel
  def subscribed
    stream_from 'home_channel'
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def speak
    ActionCable.server.broadcast 'home_channel', content: Home.all
  end
end

JMeter側の設定

ここでは、JMeterでActionCable (WebSocket) のテストを行うための説明だけ記載します。 基本的なJMeterのテストの作り方に関しては以下の記事を参考にしてみてください。 simple-minds-think-alike.hatenablog.com

②-1. HTTPヘッダー、クッキーに関する設定

WebSocketで接続するテストを追加する前に、まずは基本的な設定を行っていきます。

リクエストオリジンの設定

次に、先程Rails側で行ったリクエストオリジン許可の設定でリクエストが通るようにHTTPヘッダーの設定を行います。

  • ①HTTP Header Managerを追加

    • スレッドグループを右クリック =>「追加」 => 「設定エレメント 」=> 「HTTP Header Manager」を追加
  • ②HTTPヘッダーを設定

    • 追加ボタンを押し、以下のように設定しました。
    • 名前: 「Origin」
    • 値: 「https://load-testing.com

f:id:moritamorie:20190615163856p:plain

HTTPクッキーマネージャを追加

セッションを有効にするためにHTTPクッキーマネージャを追加します。 スレッドグループを右クリック => 「追加」 => 「設定エレメント 」=> 「HTTPクッキーマネージャ」を追加

設定変更は必要なく、Cookie Policyは初期の値「standard」のままで大丈夫です。

②-2. WebSocketのテストを追加

ここからは実際にWebSocketでアプリケーションに負荷をかけるテスト項目を追加していきます。 (ここのRequest dataの書き方を調べるのが時間がかかった。。。)

②-2-1. WebSocket Connectionを開く

スレッドグループを右クリック => 「追加」 => 「サンプラー」 => 「WebSocket Open Connection」を追加 以下はdevelopment環境で確認した際の設定ですが、Rails側の設定に合わせてPathには「/cable」を指定しました。 f:id:moritamorie:20190616123017p:plain ※この図では「シンプルコントローラ」という何の制御も行わないJMeterの要素の中に入れています。(コメントを記載するためのものです。)

この設定で、以下のエンドポイントにアクセスし、WebSocket通信を開くようになります。 ws://localhost:3000/cable

②-2-2. チャネルをサブスクライブする

Rails側で実装したHomeChanelをサブスクライブする動きを作ります。 スレッドグループを右クリック => 「追加」 => 「サンプラー」 => 「WebSocket Single Write Sampler」を追加

Data: はTextを選択し Request data: {"command":"subscribe","identifier":"{\"channel\":\"HomeChannel\"}"} を指定します。

f:id:moritamorie:20190616124059p:plain

②-2-3. Heartbeatする

ActionCableのコネクションを開くとクライアント側から3秒ごとにHeartbeatを送るようになるので、その動きも作ってみます。 スレッドグループを右クリック => 「追加」 => 「サンプラー」 => 「WebSocket Ping/Pong」を追加 f:id:moritamorie:20190616123810p:plain

Behaviorに「ping/pong」を選択します。 f:id:moritamorie:20190616124114p:plain

②-2-4. 発信する

最後にクライアント側から発信(Publish)して、ActionCableチャネルのメソッドを実行(BroadCast)されるように動きを作ります。

先程と同様に、 「WebSocket Single Write Sampler」を追加します。

Data: にはTextを選択し Request data: {"command":"message","identifier":"{\"channel\":\"HomeChannel\"}","data":"{\"action\":\"speak\"}"} を指定します

f:id:moritamorie:20190616124131p:plain

負荷テスト実行

Rails側をローカル環境でデバック実行し、JMeterの負荷テストを開始すると f:id:moritamorie:20190615233124p:plain

該当メソッドのブレイクポイントが止まり、ログには「Successfully upgraded to WebSocket」と出力されることが確認できます。

JMeter側では、「結果をツリーで表示」リスナーでリクエストを確認し

f:id:moritamorie:20190616124424p:plain Response code: 200が返ってきているので、エラーが発生せずに動いていそうです。

また、「WebSocket Single Read Sampler」をWebSocket open connectionやwriter samplerの直後に入れるとActionCableからのレスポンスを確認することができます。 f:id:moritamorie:20190616124740p:plain

「結果をツリーで確認」リスナーにおいて、以下のようにレスポンスを受け取れていることを確認できます。 f:id:moritamorie:20190616124852p:plain

参考資料