Pythonで人気のあるWebアプリケーションフレームワーク、FastAPIとFlask。どちらも非常に優れたフレームワークですが、その性能には大きな違いがある可能性があります。本記事では、FastAPIとFlaskを使用して同一のAPIを実装し、実際に負荷テストを実施し、得られた結果をもとにパフォーマンスの比較を行います。さらに、負荷テストツールであるlocust
のインストール方法とテスト実行方法も詳しく説明します。
目次
1. はじめに
Webアプリケーションのパフォーマンスは、特にAPIを提供するサービスにおいて非常に重要です。APIのレスポンスタイムやリクエスト処理能力が、ユーザー体験に直結するためです。Pythonで人気のある2つのWebアプリケーションフレームワークであるFastAPIとFlaskの性能を、実際に負荷テストを実施して比較してみましょう。
- FastAPI: 高速で、非同期処理をサポートする最新のWebフレームワーク。
- Flask: シンプルで柔軟なWebフレームワーク、特に小規模なアプリケーションに適しています。
本記事では、これら2つのフレームワークを使って同じAPIエンドポイントを実装し、locustというツールを使用して負荷テストを実施し、その結果を比較します。
2. FastAPIとFlaskの概要
FastAPIの特徴
-
非同期処理のサポート: FastAPIは
async
/await
を使って非同期処理をサポートします。これにより、I/O待機を効率的に処理し、レスポンスが非常に高速です。 -
型安全性:
Pydantic
を使用して、リクエストボディやクエリパラメータの型検査や自動バリデーションが行われます。 - APIドキュメントの自動生成: FastAPIはSwagger UIやReDocを自動で生成し、APIの仕様を簡単に確認できます。
Flaskの特徴
- シンプルさと柔軟性: Flaskは「マイクロフレームワーク」として非常に軽量であり、シンプルなAPIの作成に最適です。
-
同期型処理: Flaskは基本的に同期的に動作しますが、
gevent
やasyncio
を使うことで非同期処理も可能です。 - 拡張性: Flaskはプラグインや拡張機能が豊富で、プロジェクトに応じて簡単にカスタマイズできます。
3. 負荷テストの設計
テスト目的
FastAPIとFlaskの性能を比較するために、以下のポイントを負荷テストで測定します。
- レスポンスタイム: APIの応答速度
- リクエスト数/秒: サーバが処理できるリクエストの量
- 最大レスポンスタイム: 負荷が高くなった場合の最長の応答時間
テストシナリオ
-
エンドポイント: シンプルな
GET /hello
エンドポイントを使用します。このエンドポイントはJSONで{"message": "Hello, World!"}
というレスポンスを返します。 -
負荷テストツール:
locust
を使用してリクエストを送信し、性能を計測します。 -
テスト条件:
- 最大同時接続数: 1000人
- テスト時間: 3分
- ステップ遅延: 1~2秒
4. ライブラリのインストール方法
FastAPI、Flask、および負荷テストツールlocust
のインストール方法を解説します。
FastAPIのインストール
FastAPIをインストールするには、以下のコマンドを実行します。
pip install fastapi uvicorn
- FastAPI: Webフレームワーク本体
- Uvicorn: ASGIサーバ(FastAPIはASGIベースで動作します)
Flaskのインストール
Flaskをインストールするには、以下のコマンドを実行します。
pip install flask
locustのインストール
負荷テストツールであるlocust
をインストールします。
pip install locust
5. 実装
FastAPIとFlaskで同じエンドポイントを実装します。
FastAPIの実装
from fastapi import FastAPI
app = FastAPI()
@app.get("/hello")
async def read_root():
return {"message": "Hello, World!"}
FastAPIでは非同期関数(async def
)を使用して、非同期的にリクエストを処理します。
Flaskの実装
from flask import Flask
app = Flask(__name__)
@app.route("/hello")
def hello():
return {"message": "Hello, World!"}
if __name__ == "__main__":
app.run()
Flaskでは同期的な処理を行いますが、外部ライブラリを使って非同期処理にすることも可能です。
6. 負荷テストの実行方法
負荷テストツールであるlocust
を使用して、FastAPIとFlaskの性能を比較します。
負荷テストの作成
from locust import HttpUser, task, between
# Flaskのテストクラス
class FlaskTest(HttpUser):
host = "http://127.0.0.1:5000" # Flaskのホスト(ポート5000で起動)
wait_time = between(1, 2) # 1~2秒の間でリクエスト間隔を待機
@task
def hello(self):
self.client.get("/hello")
from locust import HttpUser, task, between
# FastAPIのテストクラス
class FastAPITest(HttpUser):
host = "http://127.0.0.1:8000" # FastAPIのホスト(ポート8000で起動)
wait_time = between(1, 2) # 1~2秒の間でリクエスト間隔を待機
@task
def hello(self):
self.client.get("/hello")
このスクリプトは、/hello
エンドポイントにGETリクエストを送信し続けます。FastAPITest
クラスがFastAPIサーバに対する負荷テストを実行し、FlaskTest
クラスがFlaskサーバに対する負荷テストを実行します。
サーバの起動
それぞれのアプリケーションを別々に起動します。
1. FastAPIのサーバを起動
uvicorn fastapi_app:app --reload --host 0.0.0.0 --port 8000
2. Flaskのサーバを起動
python flask_app.py
負荷テストの実行
locustfile_flask.py``locustfile_fastapi.py
を使って負荷テストを実行します。
locust -f locustfile_[flask/fastapi].py
実行後、ブラウザでhttp://localhost:8089
にアクセスする事で、管理画面からテストパラメータ(ユーザー数や発生率)を設定してテストを開始できます。
- Number of users: 1000(シミュレートするユーザ数)
- Ramp up: 10(ユーザを10人/秒のペースで増加)
- Run time: 3m(テスト時間)
テストが実行されると、リアルタイムで結果を確認できます。
7. 負荷テスト結果の比較
以下は、FastAPIとFlaskの負荷テスト結果です。
Flaskでのリクエスト処理数と失敗したリクエスト数の推移
FastAPIでのリクエスト処理数と失敗したリクエスト数の推移
フレームワーク | リクエスト処理数 | 失敗したリクエスト数 |
---|---|---|
FastAPI | 87297 | 0 |
Flask | 87297 | 13185 |
結果の解釈
- Flaskでは、同時接続数が増加すると、リクエストの処理に失敗する(エラー率が増加する)事がわかります。同時接続数が1000の場合は、15%程のエラー率になっており、非同期でのリクエスト処理をカスタマイズで実装しなければ厳しいかなと思います
- FastAPIではエラーが一度も出ていないことから、同時接続数が1000台では特にカスタマイズなしで安定的に利用できる事がわかります
8. 結論
どちらを選ぶべきか?
-
FastAPI:
- 高速で高い並列処理能力が求められる大規模なAPIやリアルタイム性が重要なシステムに最適
- 非同期処理を活用したい場合や、高速なレスポンスが求められる場合に特に有利
-
Flask:
- 小規模なプロジェクトや学習用途、シンプルなAPIを作成する場合に最適
- シンプルさと柔軟性を重視する場合に非常に有用
最終的な選択
- FastAPIは性能重視のアプリケーションやスケーラビリティが重要な場合に向く
- Flaskは学習コストが低く、小規模なプロジェクトやAPIに適している
どちらも素晴らしいフレームワークであり、プロジェクトの要件に応じて適切に選択することが重要です。