【Python】FastAPI vs Flask: 負荷試験による性能比較

Pythonで人気のあるWebアプリケーションフレームワーク、**FastAPI**と**Flask**。どちらも非常に優れたフレームワークですが、その性能には大きな違いがある可能性があります。本記事では、FastAPIとFlaskを使用して同一のAPIを実装し、実際に**負荷テスト**を実施し、得られた結果をもとに**パフォーマンスの比較**を行います。さらに、負荷テストツールである`locust`のインストール方法とテスト実行方法も詳しく説明します。

## 目次

1. [はじめに](#1-はじめに)
2. [FastAPIとFlaskの概要](#2-fastapiとflaskの概要)
3. [負荷テストの設計](#3-負荷テストの設計)
4. [ライブラリのインストール方法](#4-ライブラリのインストール方法)
5. [実装](#5-実装)
6. [負荷テストの実行方法](#6-負荷テストの実行方法)
7. [負荷テスト結果の比較](#7-負荷テスト結果の比較)
8. [結論](#8-結論)

---

## 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をインストールするには、以下のコマンドを実行します。

```bash
pip install fastapi uvicorn
```

- **FastAPI**: Webフレームワーク本体
- **Uvicorn**: ASGIサーバ（FastAPIはASGIベースで動作します）

### Flaskのインストール

Flaskをインストールするには、以下のコマンドを実行します。

```bash
pip install flask
```

### locustのインストール

負荷テストツールである`locust`をインストールします。

```bash
pip install locust
```

---

## 5. 実装

FastAPIとFlaskで同じエンドポイントを実装します。

### FastAPIの実装

```python:fastapi_app.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/hello")
async def read_root():
    return {"message": "Hello, World!"}
```

FastAPIでは非同期関数（`async def`）を使用して、非同期的にリクエストを処理します。

### Flaskの実装

```python:flask_app.py
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の性能を比較します。

### **負荷テストの作成**


```python:locustfile_flask.py
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")
```

```python:locustfile_fastapi.py
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のサーバを起動**

```bash
uvicorn fastapi_app:app --reload --host 0.0.0.0 --port 8000
```

#### 2. **Flaskのサーバを起動**

```bash
python flask_app.py
```

### 負荷テストの実行

`locustfile_flask.py``locustfile_fastapi.py`を使って負荷テストを実行します。

```bash
locust -f locustfile_[flask/fastapi].py
```

実行後、ブラウザで`http://localhost:8089`にアクセスする事で、管理画面からテストパラメータ（ユーザー数や発生率）を設定してテストを開始できます。

- **Number of users**: 1000（シミュレートするユーザ数）
- **Ramp up**: 10（ユーザを10人／秒のペースで増加）
- **Run time**: 3m（テスト時間）

![スクリーンショット 2024-12-14 2.37.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674933/bcfcae75-8c77-a3da-74ec-3545f60a5b4f.png)

テストが実行されると、リアルタイムで結果を確認できます。

![スクリーンショット 2024-12-14 2.13.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674933/ae5fd11c-089c-b573-0221-d73e6d02f31c.png)



---

## 7. 負荷テスト結果の比較

以下は、FastAPIとFlaskの負荷テスト結果です。

#### Flaskでのリクエスト処理数と失敗したリクエスト数の推移
![total_requests_per_second_1734112882.553.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674933/4355ebdf-cede-fd6e-d524-924be2787124.png)

#### FastAPIでのリクエスト処理数と失敗したリクエスト数の推移

![total_requests_per_second_1734111530.256.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674933/5930d7a2-0b36-31ab-45fc-50bf7d88362d.png)




| フレームワーク | リクエスト処理数 | 失敗したリクエスト数 |
|----------------|----------------------|----------------------|
| **FastAPI**    | 87297                 | 0                 |
| **Flask**      | 87297                 | 13185                 |

### 結果の解釈

- Flaskでは、同時接続数が増加すると、リクエストの処理に失敗する（エラー率が増加する）事がわかります。同時接続数が1000の場合は、15％程のエラー率になっており、非同期でのリクエスト処理をカスタマイズで実装しなければ厳しいかなと思います
- FastAPIではエラーが一度も出ていないことから、同時接続数が1000台では特にカスタマイズなしで安定的に利用できる事がわかります

## 8. 結論

### どちらを選ぶべきか？

- **FastAPI**:
  - 高速で高い並列処理能力が求められる大規模なAPIやリアルタイム性が重要なシステムに最適
  - 非同期処理を活用したい場合や、高速なレスポンスが求められる場合に特に有利

- **Flask**:
  - 小規模なプロジェクトや学習用途、シンプルなAPIを作成する場合に最適
  - シンプルさと柔軟性を重視する場合に非常に有用

### 最終的な選択

- **FastAPI**は性能重視のアプリケーションやスケーラビリティが重要な場合に向く
- **Flask**は学習コストが低く、小規模なプロジェクトやAPIに適している

どちらも素晴らしいフレームワークであり、プロジェクトの要件に応じて適切に選択することが重要です。