17
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

懇親会での障害対応はもうこりごり!JMeterを使ったRailsアプリの負荷テストの流れ

Last updated at Posted at 2019-12-14

これは Money Forward Advent Calendar 2019 🎄 14日目の記事です。

こんにちは! @machisukeです。
マネーフォワードでは、半年に1回全社員集まっての半期総会を開催しています。
そして昨日、2019年12月13日がちょうど半期総会でした。

半期総会後の全社懇親会 :beers: で、僕たちマネーフォワード新卒はあるリベンジを果たそうとしていました :fire:

もうサーバーは落とさない。

今年6月に開催された半期総会で、僕たちは懇親会のコンテンツとして「MFクイズダービー」を担当しました。

スマホを使ってリアルタイムに順位が発表されるという内容に大盛り上がりでコンテンツはスタート。
会場の盛り上がりを見た僕たちは、作った甲斐があったなあと安堵していました。しかしその直後に事件は起きます。300名を超える参加者にサーバーが耐えきれず、途中でシステムが止まっていたのです。詳細は弊社のエンジニアブログ「新卒が社内懇親会アプリを開発したら、障害対応まで経験できた話」をぜひ読んでみてください。

このままでは終われないと、半年後の全社懇親会でのリベンジを心に決めました。

リベンジの過程で、僕は負荷テストを実施し、その時使った「JMeter」がとても便利で面白かったので、皆さんに手順を共有したいと思います。
(※ちなみに、今回の懇親会が成功したのかどうかは誰かがブログを書くと思うので楽しみに待ちましょう。)

JMeter上でのテスト計画と結果のイメージ

このような感じで、JMeterを使ってクイズの参加登録(sign_up)、クイズ取得、クイズ回答などが正しく動作しているか検証できます。本番は50チームで行いますが、テストは300チームで行いました。
image.png

image.png

Railsアプリの負荷テストに挑戦してみよう :rocket:

今回は負荷テストを検証するアプリケーションとしてRailsチュートリアルで作成するSampleAppを拝借したいと思います。
SampleAppはTwitterのように「Micropost」を投稿するサービスです。

30ユーザーを同時アクセスさせ、1秒あたり1投稿させても、アプリは落ちることなく動き続けるでしょうか!?

SampleAppの画面 :point_down:
image.png

環境

  • Mac OS Mojave
  • JMeter 5.2.1
  • ruby 2.6.5
  • rails 5.1.2 (sample_appの最新版に合わせました)

手順

1. jmeterインストール

$ brew install jmeter

2. Railsアプリケーションの起動

$ git clone https://github.com/yasslab/sample_apps.git
$ cd sample_apps/5_1_2/ch14
$ bundle install
$ bundle exec rails db:create
$ bundle exec rails db:migrate

今回はメール認証を強制的にスキップするため、app/controllers/users_controller.rbに変更を加えます。
①、②の変更を行ってください。

app/controllers/users_controller.rb
  # POST /users
  def create
    @user = User.new(user_params)
    if @user.save # => Validation
      # Sucess
      # ①↓コメントアウト
      #@user.send_activation_email
      # ②↓追加
      @user.activate        
      flash[:info] = "Please check your email to activate your account."
      redirect_to root_url
    else
      # Failure
      render 'new'
    end
  end

起動

$ bundle exec rails s

http://localhost:3000 にアクセスすると画面が開かれるはずです。

3. JMeter起動

$ jmeter

image.png

HTTP Request Defaults作成

Test Plan 右クリック > Add > Config Element > HTTP Request Defaults
起動しているサーバーのアクセス情報を入れます。

image.png

Thread Gropu(ユーザーグループ)の作成

Test Plan 右クリック > Add > Threads (Users) -> Thread Group
同時にアクセスするユーザー数を適当に決めます。
今回は、30人のユーザーが30秒の間に操作を開始するという設定にします。

image.png

ユーザ毎に登録内容を変える準備

i番目のユーザーは

name: name_i
email: name_i@example.com
password: password_i

としましょう。

Test Plan 右リクック > Add > Config Elemennt > Counter
coutnerという変数名で取得できるようにします。

image.png

4. JMeterでユーザー登録、ログインさせる

ユーザー登録・ログインのリクエストをグルーピングする

Thread Group 右クリック > Add > Logic Controller > Simple Controller

名前はsign_up/sign_inにします
image.png

ユーザー登録(sign_up)フォームの取得

sign_up/sign_in 右クリック > Add > Sampler > HTTP Request

名前はsign_upフォーム取得にします。
sign_upフォームが表示されるURLは
http://localhost:3000/signupなので、Pathにsignupを入力します

image.png

正しくリクエストできているか検証

Test Plan 右クリック > Add > Listener > View Results Treeを追加

JMeterの上側の緑色の三角ボタンを押してテストをスタートするとリクエスト結果が出ます。

image.png

AuthenticityTokenの取得

今回使うRailsアプリはCSRF対策が施されているので、AuthenticityTokenをリクエストパラメーターに含める必要があります。
AuthenticityTokenは、「登録フォーム取得」のレスポンスに含まれています。

これは、Regular Expression Extractorで抜き出します。

sign_upフォーム取得 右クリック > Add > Post Processors > Regular Expression Extractor
tokenを正規表現でキャプチャして、authenticity_tokenという変数に代入します。

image.png

ユーザー登録

sign_up/sign_in 右クリック > Add > Sampler > HTTP Request
名前はsign_upにします。
urlencodeにチェック入れるのを忘れないようにしましょう。

image.png

ログイン状態を保持できるようにする(Cookie)

Test Plan 右クリック > Add > Config Element > HTTP Cookie Manager

追加するだけでOKです。

image.png

ログイン

登録同様、下記の手順を行います。

  1. フォーム取得
  2. authenticity_token抜き出し
  3. ログイン

sign_up/sign_in 右クリック > Add > Sampler > HTTP Request
名前はsign_inフォーム取得にします。
image.png

sign_inフォーム取得 右クリック > Add > Post Processors > Regular Expression Extractor
tokenを正規表現でキャプチャして、authenticity_tokenという変数に代入します。

image.png

sign_up/sign_in 右クリック > Add > Sampler > HTTP Request
名前はsign_inにします。
image.png

試しにログインしてみる

ブラウザでhttp://localhost:3000/loginを開き、適当なユーザーでログインして、http://localhost:3000/usersにアクセスしてみる。

ユーザーが作られているのがわかりますね。
image.png

※テストを実行すると、ユーザーが登録されてDBに保存されます。
テストの度にDBをリセットするとユーザー登録から正しくテストを行うことができます。

$ bundle exec rails db:migrate:reset

5. 各ユーザーに、Micropost(Tweet)を50件登録させる

sign_in/sign_up同様、下記の手順でMicropostを投稿します。

  1. フォーム取得
  2. authenticity_token抜き出し
  3. 登録
投稿リクエストをグルーピングする

Thread Group 右クリック > Add > Logic Controller > Simple Controller

名前はMicropost投稿にします
image.png

さらに
Thread Group 右クリック > Add > Logic Controller > Loop Controller

名前は50回投稿にします

image.png

投稿ごとにメッセージを分けるための変数を用意

50回投稿 右リクック > Add > Config Element > Counter
micropost_coutnerという変数名で取得できるようにします。

image.png

投稿フォーム取得

50回投稿 右クリック > Add > Sampler > HTTP Request
名前は投稿フォーム取得にします。
URLはhttp://localhost:3000なので、pathは何も入力しません。

image.png

AuthenticityToken取得

投稿フォーム取得 右クリック > Add > Post Processors > Regular Expression Extractor
tokenを正規表現でキャプチャして、authenticity_tokenという変数に代入します。

image.png

投稿

50回投稿 右クリック > Add > Sampler > HTTP Request
名前は投稿にします。

image.png

投稿間隔の調整

50回投稿 右クリック > Add > Timer > Constant Timer
投稿間隔を一人につき、1秒1回に調整します。

image.png

6. Listener(レポート機能)の設定

テストが終わるまでのレスポンスタイムの遷移を見る

Test Plan 右クリック > Add > Listener > jp@gc - Response Times Over Time

7. テスト実行

JMeterの上部にある、緑色の三角ボタンを押したら始まります。

8. テスト結果

サーバは落ちませんでした :tada:
ただ、ところどころピークが生まれていて、ログを見るとDBのRollbackが行われている様子。同時書き込みに弱いSQLiteだから発生したRollbackでしょうか・・?

image.png

まとめ

JMeterは気軽に負荷テストを行えるツールでした。

どのようにインフラ/実装を変えれば、レスポンスタイムが短くなるかを考えてみるのは、課題として面白そうですね。

17
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?