Python Webの未来: ASGIと非同期処理

![Group39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/d8dbf168-3940-cd39-8e97-920243d5f142.png)

[Leapcell: Python Webホスティング、非同期タスク、およびRedisのための最高のサーバレスプラットフォーム](https://leapcell.io/?lc_t=q_pasgi)

# FastAPI開発におけるASGIとUvicornの理解

FastAPIを使って開発する際、Uvicornサーバを使用する必要があります。初心者の方は、なぜそうなのか不思議に思うかもしれません。今日は、この疑問に答えましょう。

## Uvicorn
まず、HTTPを使って簡単なHTTPリクエストを実装してみましょう：
```python
import json

# ヘッダー内のバイトを文字列に変換するヘルパー関数を定義
def convert_bytes_to_str(data):
    if isinstance(data, bytes):
        return data.decode('utf-8')
    if isinstance(data, tuple):
        return tuple(convert_bytes_to_str(item) for item in data)
    if isinstance(data, list):
        return [convert_bytes_to_str(item) for item in data]
    if isinstance(data, dict):
        return {key: convert_bytes_to_str(value) for key, value in data.items()}
    return data


async def app(scope, receive, send):
    # print(scope)
    data = convert_bytes_to_str(scope)
    print(json.dumps(data, indent=4))
    # リクエストのタイプをチェック
    if scope['type'] == 'http':
        # HTTPリクエストボディを待つ
        event = await receive()

        # レスポンスコンテンツ
        response_body = json.dumps({"message": "Hello, ASGI!"}).encode('utf-8')

        # HTTPレスポンスヘッダーを送信
        await send({
            'type': 'http.response.start',
           'status': 200,
            'headers': [
                (b'content-type', b'application/json'),
            ],
        })

        # HTTPレスポンスボディを送信
        await send({
            'type': 'http.response.body',
            'body': response_body,
        })
```
`scope` フィールドにはバイナリ文字列が含まれているため、変換関数 `convert_bytes_to_str` が必要です。簡単に分析してみましょう。リクエストパスやメソッドに関する情報は `scope` の中にあります。`scope` はHTTP以外のものもサポートしているため、現在のプロトコルのタイプを判断するために `type` を使用する必要があります。そして、`receive` 関数を使ってリクエストボディを受け取ります。次に、`json.dumps` を使ってレスポンスボディを作成し、`encode` でエンコードします。最後に、リクエストヘッダーとリクエストボディを送信します。

ここでは、2回データを送信しています。この設計は非同期プログラミングの要件を満たし、異なるアプリケーションシナリオ（ストリーミングレスポンス）に対する処理能力を高めます。実際のプロジェクトの理解を深めることで、今では理論的な基礎からASGIを理解することができます。

## ASGI
非同期サーバゲートウェイインターフェイス（Asynchronous Server Gateway Interface、ASGI）は、非同期Webアプリケーションを構築するためのPythonプロトコルです。

### ASGIの主な機能
- **非同期サポート**：非同期プログラミングを可能にします。
- **マルチプロトコルサポート**：WebSocketやロングポーリングなどのプロトコルをサポートしており、http/https/websocketをサポートします。
- **並列処理サポート**：複数のリクエストを同時に処理できます。
- **通信**：アプリケーションとサーバ、またアプリケーションの異なる部分同士が、メッセージの送受信により相互作用します。

### ASGIプロトコルの構成
- **アプリケーションインターフェイス**
    - ASGIアプリケーションインターフェイスは、アプリケーションがASGIサーバとどのように相互作用するかを定義します。ASGIアプリケーションは、通常は非同期関数である呼び出し可能なオブジェクトで、2つのパラメータ `scope` と `receive` を受け取り、`send` 非同期ジェネレータを返します。
        - **scope**：リクエストに関する情報、例えばリクエストタイプ（HTTPまたはWebSocket）、パス、クエリ文字列、サーバ情報などを含む辞書です。
        - **receive**：ASGIサーバからイベントを受け取るために使用される非同期呼び出しです。
        - **send**：ASGIサーバにイベントを返すために使用される非同期ジェネレータです。
- **サーバインターフェイス**
    - サーバインターフェイスの主な責務は以下の通りです：
        - クライアントからの接続を受け付ける。
        - 各接続に対して `scope` を作成する。
        - アプリケーションの `receive` と `send` メソッドを呼び出してイベントを渡す。
        - ネットワーク例外を処理し、接続を閉じる。
- **イベントループインターフェイス**
    - イベントループインターフェイスはASGIプロトコルの暗黙的な部分です。ASGIサーバによって管理され、ASGIプロトコルによって直接定義されるものではありません。
    - イベントループは非同期タスクのスケジューリングと実行を担当し、これが非同期プログラミングの核心です。その主な機能は以下の通りです：
        - 非同期タスクの実行とスケジューリング。
        - ネットワークリクエストなどの非同期I/O操作の管理。
        - コールバック関数と非同期ジェネレータの処理。
    - ASGIでは、イベントループは通常以下のPythonライブラリによって提供されます：
        - **asyncio**：Python標準ライブラリ内の非同期I/Oフレームワーク。
        - **uvloop**：`libuv`に基づく非同期イベントループで、Uvicornサーバとともによく使用されます。

ASGIサーバとアプリケーションは、イベントループに依存して非同期操作を実行し、多数の同時接続を効率的に処理できるようになります。

### ASGIイベント
ASGIのイベント駆動型モデルは、ライフサイクル管理（起動とシャットダウン）、HTTPリクエストの処理、WebSocket接続管理を行います。異なる種類のイベントを通じて、開発者は接続とデータフローを正確に制御し、非同期および並列処理を実現できます。
- **ライフサイクルイベント**
    - ライフサイクルイベントは、ASGIアプリケーションの起動とシャットダウンサイクルに関連付けられています。通常、初期化とクリーンアップタスクを実行するために使用されます。
        - **lifespan.startup**
        - **lifespan.shutdown**
- **HTTPイベント**
    - ASGIによるHTTPリクエストの処理は、複数のイベントに分割され、各HTTPリクエストの詳細を管理できます。
        - **http.request**
        - **http.response.start**
        - **http.response.body**
        - **http.disconnect**
        - **websocket.send**
- **WebSocketイベント**
    - ASGIはWebSocket接続をサポートし、双方向通信を可能にします。
        - **websocket.connect**
        - **websocket.receive**

### ASGIライフサイクル
ASGIアプリケーションのライフサイクルとは、アプリケーションが起動とシャットダウンの間に経る段階を指します。これは、ライフサイクルイベントチャネルによって管理されます。
- `scope['type']: lifespan`
- `message['type']`：起動とシャットダウンのメッセージ：`lifespan.startup/lifespan.shutdown`
- 送信タイプ：`{'type': 'lifespan.shutdown.complete'}`

ASGIのライフサイクルは、アプリケーションの起動とシャットダウン、およびリクエストの確立、処理、応答、シャットダウンの2つの部分に分けられます。

## Uvicornとアプリケーション層
前述のとおり、UvicornはASGIサーバ層を実装しています。しかし、アプリケーション層ではレベルが低すぎるため、あまり使いやすくありません。そのため、多くの上位レベルのフレームワークが登場しました：
- **Starlette**：高性能なWebサービスを構築するための軽量なASGIフレームワーク/ツールキット。FastAPIの基本コンポーネントであり、単独でも簡単なWebアプリケーションを作成することができます。
- **FastAPI**：APIを構築するための最新の高速（高性能）なWebフレームワークで、Python 3.6以降の型ヒントを使用します。Starletteと標準のPython型ヒントに基づいており、自動的なデータ検証とシリアル化、または対話型のAPIドキュメント生成を提供します。

ここでは他の点については詳細を述べる必要はありません。情報が多すぎると圧倒されるかもしれません。Uvicornは `app` が必要なので、これら2つのフレームワークでは、インスタンス化されたオブジェクトが `app` となります。そして、Uvicorn CLIを使ってアプリケーションを起動します：
```bash
uvicorn main:app --reload
```

## まとめ
本記事では主にUvicornを通じてPythonのWeb ASGIプロトコルを探りました。ASGIはPythonの非同期サーバゲートウェイインターフェイスで、現代のPython Webプログラムの非同期、並列、マルチプロトコル機能を統合しています。

# [Leapcell: Python Webホスティング、非同期タスク、およびRedisのための最高のサーバレスプラットフォーム](https://leapcell.io/?lc_t=q_pasgi)

[![barndpic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/ae2d21c3-a4cc-33c5-b51d-fcd5e19e5415.png)](https://leapcell.io/?lc_t=q_pasgi)


最後に、Pythonプロジェクトのデプロイに最適なプラットフォームをおすすめします：[Leapcell](https://leapcell.io/?lc_t=q_pasgi)

### 1. マルチ言語サポート
- JavaScript、Python、Go、またはRustで開発できます。

### 2. 無料で無制限のプロジェクトをデプロイ
- 使用量に応じてのみ課金 — リクエストがなければ料金は発生しません。

### 3. 圧倒的なコスト効率
- 使い放題で、アイドル時の料金はかかりません。
- 例：25ドルで平均応答時間60msで694万回のリクエストをサポート。

### 4. シンプルな開発者体験
- 直感的なUIで簡単にセットアップできます。
- 完全自動化されたCI/CDパイプラインとGitOps統合。
- アクション可能な洞察のためのリアルタイムメトリクスとログ。

### 5. 簡単なスケーラビリティと高性能
- 高い同時接続数を簡単に処理できる自動スケーリング。
- オペレーションオーバーヘッドはゼロ — 構築に集中できます。

[![Frame3-withpadding2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/7fbaf9cd-536b-ed08-0bbe-6a997feb0b58.png)](https://leapcell.io/?lc_t=q_pasgi)


[ドキュメントで詳細を確認！](https://docs.leapcell.io/)

Leapcell Twitter：[https://x.com/LeapcellHQ](https://x.com/LeapcellHQ)