見出し画像

PythonのWEBアプリケーションの基本的なパフォーマンス改善(パフォーマンスを測定)

まずはWEBアプリケーションのパフォーマンスが良いか悪いかを測定していきます。そもそもパフォーマンスが良い、悪いとはどういう事を指すのでしょうか。以下にいくつか並べてみました。

・処理能力(スループット)が高い
・メモリー消費量が少ない
・リクエストを送信してレスポンスが返ってくるまでの時間(レスポンスタイム)が短い

いろんな指標があるものの、今回は以下の点について注目します。

・リクエストを送信して、レスポンスが返ってくるまでの時間(レスポンスタイム)

ここでは、ApacheBenchというパフォーマンス測定ツールを使用していきます。ちなみにタイトルで「Python」と付けているものの、ApacheBenchを使って測定をするのは、Python以外でも可能です。

ApacheBenchとは

ApacheBenchとは、ApacheというWEBサーバーに同梱されているWEBサーバーの性能測定ツールです。WEBサーバーが不特定多数からのアクセスが来た際に、どれくらいの負荷に耐えられるかを検証する際などに使用します。

ApacheBenchは、非常に細かい指定ができるツールであり、同時アクセス数を決めたり、合計で何回アクセスするかなどといった細かい指定をしての性能測定が可能になります。

ApacheBenchを使う際の注意

ApacheBenchは、特定のサーバーに対して大量のリクエストを発行するため、いわゆるDOS攻撃が可能になります。そのため、自分の管理下にないWEBサーバーなどに対しては使用しないでください。

あくまで使用する際は、自分の管理下にあるWEBサーバーなどに対して、負荷検証をする際などに使用してください。

インストール作業

それでは実際にインストール作業を行っていきます。自分の環境だとUbuntuを使用しているため、以下のコマンドでApacheBenchをインストールできます。ちなみにMacを使用していれば、標準で入っているそうです。

$ sudo apt-get install apache2-utils

以下のコマンドでApacheBenchがきちんとインストールされたかどうかを確認できます。

# apachebenchのバージョン確認

$ ab -V
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

ApacheBenchを使用してみる

それではApacheBenchを実際に使用してみましょう。以下のコマンドで実際にリクエストを発行できます。

URLがhttp://127.0.0.1:8000に対してリクエストを発行してみましょう。リクエスト回数を1000回、コネクション数(接続数)を並列で100でリクエストをします。

$ ab -n 1000 -c 100 http://127.0.0.1:8000

abコマンドの、それぞれのオプションを説明します。

n - 生成するリクエスト数
c - 並列実行する数(コネクション数)
t - サーバからのレスポンスの待ち時間(秒)を指定

A - Basic認証でユーザー名とパスワードを指定
・ユーザー名:パスワード
P - 認証の必要なプロキシをする際に指定
・ユーザー名:パスワード
X - プロキシ経由でリクエストをする場合に指定
・プロキシサーバ名:ポート番号

V - バージョンを表示
h - ヘルプを表示

実行結果は以下のように出力されます。

Server Software:        WSGIServer/0.2
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        16510 bytes

Concurrency Level:      10
Time taken for tests:   7.167 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      16681000 bytes
HTML transferred:       16510000 bytes
Requests per second:    139.52 [#/sec] (mean)
Time per request:       71.674 [ms] (mean)
Time per request:       7.167 [ms] (mean, across all concurrent requests)
Transfer rate:          2272.80 [Kbytes/sec] received

Connection Times (ms)
             min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:    20   71  17.6     68     132
Waiting:        4   63  16.1     61     127
Total:         20   71  17.6     68     132

Percentage of the requests served within a certain time (ms)
 50%     68
 66%     75
 75%     81
 80%     84
 90%     97
 95%    105
 98%    114
 99%    119
100%    132 (longest request)

それぞれの項目について説明します。

Complete requests

成功したリクエスト数の数を示します。上記のComplete requestsの項目を見れば、1000と表示されているため、全てのリクエストが成功したことになります。

Complete requests:      1000
Failed requests:        0

Requests per second

1秒間で処理されたリクエスト数を示します。上記のRequests per secondでは1秒間に平均で139リクエストを処理したことを示しています。

Requests per second:    139.52 [#/sec] (mean)

Connection Times

1リクエストに要した時間の詳細がわかります。Connection Timesの4つの項目は以下のようになっています。

Connect(接続時間): 接続確立までに要した時間
Processing(処理時間): サーバ側での処理時間
Waiting(待ち時間): 接続確立からクライアントがレスポンスを受け取るまでの時間
Connection Times (ms)
             min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:    20   71  17.6     68     132
Waiting:        4   63  16.1     61     127
Total:         20   71  17.6     68     132

上記の各列の要素は、左から最小値(min)、平均値(mean)、標準偏差([+/-sd])、中央値(median)、最大値(max)を示しています。それぞれの数字の単位はミリ秒です。

上記の場合の内訳だと、サーバーへの接続が確立してレスポンスを受け取るまでの時間(Waiting)は、平均で63ミリ秒、つまりはリクエストからレスポンスが返ってくるまでの時間は0.06秒だというのがわかります。
最長は0.127秒、最小だと0.004秒でレスポンスが返ってきています。

パフォーマンス測定の指標を考える

ApacheBenchの基本的な使い方について知ったものの、どういった具体的な事例を元に負荷検証をしていけばよいでしょうか。

主に以下の事例を元にApacheBenchで負荷検証を行っていきます。

アクセス数が2〜3倍に増えた場合にサーバーは負荷に耐えられるか

今までは問題なくリクエストを捌けていたものの、もし明日急にリクエストが倍に増えた場合はどれだけ負荷に耐えられるのかといった検証をしていきます。

例えばリクエスト数が500で、並列のコネクション数を50で設定してみます。これは50人が10リクエスト同時にアクセスした場合の検証です。(50人 x 10req = 500req)

これはつまり、通常時は50人からの同時アクセスで500リクエストを問題なく捌けるWEBアプリケーションというのを想定しています。

$ ab -n 500 -c 50 http://127.0.0.1:8000/

自分の環境では、上記の結果が以下のように表示されました。特に問題なく捌けていることがわかります。

Server Software:        WSGIServer/0.2
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        16510 bytes

Concurrency Level:      50
Time taken for tests:   14.235 seconds
Complete requests:      500
Failed requests:        0
Total transferred:      8340500 bytes
HTML transferred:       8255000 bytes
Requests per second:    35.12 [#/sec] (mean)
Time per request:       1423.505 [ms] (mean)
Time per request:       28.470 [ms] (mean, across all concurrent requests)
Transfer rate:          572.18 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   93 290.7      0    1032
Processing:    37  443 1603.8     76   13231
Waiting:       24  435 1603.7     69   13231
Total:         37  535 1821.8     76   14234

Percentage of the requests served within a certain time (ms)
  50%     76
  66%     85
  75%     91
  80%     96
  90%    297
  95%   4330
  98%   7633
  99%   7672
 100%  14234 (longest request)

以下に簡単に結果をまとめました。

・コネクション数が50、リクエスト数が500

Complete requests: 成功したリクエスト数
- 500
Requests per second: 1秒間で処理されたリクエスト数
- 35.12 秒 (平均)
Connection Times (Waiting): レスポンスタイム
- 435 ミリ秒(0.435秒) (平均)

それでは更に今までより2倍のアクセスが来た場合の検証を行っていきます。

例えばリクエスト数を2倍の1000にし、並列のコネクション数を同じく2倍の100で設定してみます。これは100人が10リクエスト同時にアクセスした場合の検証です。(100人 x 10req = 1000req)

これは、もし明日などに問題なく捌ける通常時より2倍ものアクセスが来てしまった場合に、問題なくアクセスを捌けるかの検証です。

$ ab -n 1000 -c 100 http://127.0.0.1:8000/

自分の環境では以下のような結果が表示されました。こちらも問題なく捌けてみたものの、上記の通常時の結果と比べてみます。

Server Software:        WSGIServer/0.2
Server Hostname:        127.0.0.1
Server Port:            8000
Document Path:          /
Document Length:        16510 bytes
Concurrency Level:      100
Time taken for tests:   55.421 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      16681000 bytes
HTML transferred:       16510000 bytes
Requests per second:    18.04 [#/sec] (mean)
Time per request:       5542.077 [ms] (mean)
Time per request:       55.421 [ms] (mean, across all concurrent requests)
Transfer rate:          293.93 [Kbytes/sec] received
Connection Times (ms)
             min  mean[+/-sd] median   max
Connect:        0  209 440.2      0    3025
Processing:    40 1517 7022.6     91   54395
Waiting:       18 1507 7023.4     82   54394
Total:         40 1726 7199.5     94   55419
Percentage of the requests served within a certain time (ms)
 50%     94
 66%    109
 75%    133
 80%    283
 90%   1157
 95%   7759
 98%  27700
 99%  55369
100%  55419 (longest request)
・コネクション数が100、リクエスト数が1000

Complete requests: 成功したリクエスト数
- 1000
Requests per second: 1秒間で処理されたリクエスト数
- 18.04 秒 (平均)
Connection Times (Waiting): レスポンスタイム
- 1507 ミリ秒(1.507秒) (平均)

先程の通常時と比べるとちょうど2倍くらいパフォーマンスが下がっていることがわかります。

「倍以上のアクセス数での負荷検証」の点で注目すべきは以下のような点です。先程の検証結果とそれぞれを比べてみます。

・全てのアクセスが正常に処理されたか
-> 1000アクセス全てを捌いているので問題ない

・レスポンスタイムに劣化があるか
-> 平均で0.435秒でレスポンスを返せてたものの、1.507秒とかかってしまっているため、レスポンスタイムに劣化があることがわかる。

・1秒毎に捌けるアクセス数に劣化があるか
-> 1秒間に捌けるのが35.12から18.04と下がっているため、劣化があることがわかる。

ApacheBenchでは検証できないものの、以下の点でもパフォーマンスの測定を考えたりもします。

・サーバーのCPUが高負荷状態になっていないかを確認する
・WEBサーバーの接続先であるデータストアやキャッシュ側の負荷があがっていないかを確認(例えばDBサーバーなど)

ApacheBenchの用途についてのまとめ

ApacheBenchは簡単な負荷検証などの際に使用できるものの、あくまでも測定対象が単一URLであり、なおかつ単一クライアントからのみの場合です。

本来のWEBアプリケーションは、ユーザーからのアクセスがある際に、いくつかのシナリオが存在します。
例えば、ログインページからのログイン、その後マイページを表示し、自分のコンテンツ一覧を表示するなど、複数のページへのアクセスが同時に発生するわけです。
残念ながらApacheBenchにはこういった検証を行う機能はありません。

そこでシナリオを記述でき、なおかつ複数のクライアントから同時に負荷をかけて、負荷検証が行えるツールがあります。しかもPythonを用いて、シナリオを記述できます。
Locust.ioというものです。。Locust.ioはPythonで書かれた分散負荷試験ツールです。

使い方などの詳細は以下のQiitaのページが参考になります。別途Locust.ioについての記事を書く予定です。

参考資料

Apache Benchでサクッと性能テスト
【初心者向け】ApacheBench入門
Apache Bench(ab)を使って初めての負荷検証を行う

この記事が気に入ったらサポートをしてみませんか?