Flask 2.x における非同期ビューと WebSocket 統合
Olivia Novak
Dev Intern · Leapcell

バックエンド開発の進化し続ける状況において、応答性とリアルタイム機能は、望ましい機能から必須の要件へと移行しました。従来の同期 Python フレームワークは堅牢ですが、データベースクエリ、外部API呼び出し、ロングポーリングリクエストなどの I/O バウンドな操作を扱う際にはしばしば限界に直面します。これにより、特に高負荷下でのレイテンシの増加やスループットの低下につながる可能性があります。Python における async/await
の登場と、Flask 2.x へのシームレスな統合により、これらの課題に対する強力なソリューションが提供され、開発者は高い並行性とスケーラビリティを備えた Web アプリケーションを構築できるようになります。さらに、クライアントとサーバー間のインスタントな双方向通信に対する需要の高まりは、WebSocket を顕著にし、インタラクティブな機能の開発方法を変革しました。この記事では、Flask 2.x が非同期ビューを活用してパフォーマンスを向上させ、WebSocket を統合してリッチでリアルタイムなユーザーエクスペリエンスを実現する方法を掘り下げ、モダンで応答性の高い Web サービスを構築するための基盤を提供します。
コアコンセプトとメカニズム
実装の詳細に入る前に、Flask における非同期プログラミングと WebSocket 通信の理解に不可欠ないくつかの基本概念を明確にしましょう。
非同期プログラミング (async/await): このパラダイムは、プログラムが I/O 操作(例:データベースからの読み取り)を開始し、それが完了するのを待つのではなく、他のタスクの実行に切り替えることを可能にします。I/O 操作が完了すると、プログラムは元のタスクを再開できます。Python では、async
は「コルーチン」(非同期関数)を定義し、await
はコルーチン(または別のコルーチンや I/O 操作など)が完了するまで、コルーチンの実行を一時停止します。このノンブロッキングの性質は、コンテキストスイッチやリソース競合によるオーバーヘッドをしばしば伴う従来のマルチスレッディングに依存することなく、並行処理を大幅に改善します。
ASGI (Asynchronous Server Gateway Interface): WSGI(Web Server Gateway Interface)が Python Web サーバーと同期 Web アプリケーション間のインターフェースを標準化したように、ASGI は非同期アプリケーションに対しても同様の標準化を行います。Flask 2.x の ASGI サポートにより、Uvicorn や Hypercorn のような非同期サーバー上で実行でき、これらは効率的に並行非同期リクエストを処理するように設計されています。
WebSockets: HTTP のリクエスト-レスポンスモデルとは異なり、WebSockets は単一の長寿命 TCP 接続を介してフルデュプレックス通信チャネルを提供します。これは、クライアントとサーバーの両方がいつでも互いにメッセージを送信できることを意味し、チャットアプリケーション、ライブダッシュボード、共同作業ツールなどのリアルタイムインタラクションを促進します。初期の HTTP ハンドシェイクの後、接続は WebSocket に「アップグレード」され、永続的で低遅延のデータ交換が可能になります。
Flask 2.x における非同期ビュー
Flask 2.x は、非同期ビュー関数を定義できるようにすることで async/await
を採用しています。これは、サーバープロセス全体をブロックする可能性のある I/O バウンドな操作に特に役立ちます。
同期 Flask ビューを検討してください:
from flask import Flask, jsonify import time app = Flask(__name__) @app.route("/sync-data") def get_sync_data(): time.sleep(2) # ブロッキング I/O 操作をシミュレート return jsonify({"message": "Data fetched synchronously"}) if __name__ == "__main__": app.run(debug=True)
この例では、複数のクライアントが同時に /sync-data
にアクセスした場合、各リクエストは 2 秒間の time.sleep()
が順次完了するのを待たなければならず、パフォーマンスの低下につながります。
次に、非同期ビューで再記述してみましょう:
from flask import Flask, jsonify import asyncio app = Flask(__name__) @app.route("/async-data") async def get_async_data(): await asyncio.sleep(2) # ノンブロッキング I/O 操作をシミュレート return jsonify({"message": "Data fetched asynchronously"}) if __name__ == "__main__": # 非同期ビューを実行するには、Uvicorn のような ASGI サーバーが必要です # 通常は `uvicorn your_app_file:app --reload` で実行します pass
これを真に非同期で実行するには、ASGI サーバーが必要です。たとえば、ファイル名が app.py
の場合、uvicorn app:app --reload
を使用して実行します。複数の非同期リクエストが /async-data
にヒットすると、asyncio.sleep(2)
はサーバーのスレッドをブロックせず、代わりにスリープが完了するのを待つ間にサーバーは他のリクエストやタスクの処理に切り替えることができます。これにより、アプリケーションが並行接続を処理する能力が大幅に向上します。外部 API 呼び出しには aiohttp
、PostgreSQL には asyncpg
、MySQL には aiomysql
を同様のノンブロッキング方式で統合できます。
WebSocket サポート
Flask 自体には、ネイティブの組み込み WebSocket サポートはありません。しかし、Flask-SocketIO
のような Flask 拡張機能を使用するか、Flask と共に WebSocket をサポートする ASGI ツールキットを直接使用することで、シームレスに統合できます。シンプルさと広く普及しているため、ここでは Flask-SocketIO
に焦点を当てます。これはより高レベルの抽象化を提供します。
まず、必要なライブラリをインストールします:
pip install Flask Flask-SocketIO eventlet
eventlet
(または gevent
) は、Python の標準ライブラリをノンブロッキングにするためにパッチを当てる Flask-SocketIO
によって必要とされます。ASGI を使用した真の async/await
WebSocket の場合、python-socketio
を ASGI サーバーと直接使用することが考えられます。
以下は、Flask-SocketIO
を使用したシンプルなチャットアプリケーションの例です:
from flask import Flask, render_template from flask_socketio import SocketIO, emit app = Flask(__name__) app.config['SECRET_KEY'] = 'your_secret_key' # 強力な秘密鍵に置き換えてください socketio = SocketIO(app, async_mode='eventlet') # 'async_mode' は 'gevent', 'threading', または uvicorn を使用した 'asyncio' にすることができます @app.route('/') def index(): return render_template('index.html') @socketio.on('message') def handle_message(data): print(f"Received message: {data['data']}") emit('my response', {'data': data['data']}, broadcast=True) @socketio.on('connect') def test_connect(): print("Client connected") emit('my response', {'data': 'Connected!'}) @socketio.on('disconnect') def test_disconnect(): print("Client disconnected") if __name__ == '__main__': # Eventlet の場合、`socketio.run()` で実行します socketio.run(app, debug=True)
そして対応する templates/index.html
ファイル:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Flask WebSocket Chat</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js"></script> <script type="text/javascript" charset="utf-8"> var socket = io(); socket.on('connect', function() { socket.emit('message', {data: 'I\'m connected!'}); }); socket.on('my response', function(msg) { document.getElementById('messages').innerHTML += '<p>' + msg.data + '</p>'; }); function sendMessage() { var input = document.getElementById('chatInput'); socket.emit('message', {data: input.value}); input.value = ''; } </script> </head> <body> <h1>Flask WebSocket Chat</h1> <input type="text" id="chatInput" onkeydown="if (event.keyCode == 13) sendMessage()"> <button onclick="sendMessage()">Send</button> <div id="messages"></div> </body> </html>
この例では、@socketio.on('message')
は、クライアントが message
イベントを送信するたびに実行される関数をデコレートします。emit('my response', ..., broadcast=True)
行は、受信したメッセージを接続されているすべてのクライアントに返送し、効果的にシンプルなチャットルームを作成します。
非同期ビューと WebSocket の組み合わせ (ASGI ドリブン)
Flask-SocketIO
と eventlet
が WebSocket 機能を備えているのに対し、ASGI サーバーを使用して Flask 2.x の async/await
ネイティブエクスペリエンスを真に実現するには、python-socketio
を直接統合することが考えられます。このアプローチは、Flask 2.x コアにおける async/await
のパラダイムによりよく適合します。
まず、ASGI 用に特別に設計された python-socketio
をインストールします:
pip install Flask python-socketio aiohttp uvicorn
以下は例です:
from flask import Flask, render_template_string import socketio import asyncio # Socket.IO サーバーを作成 sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*') # Flask アプリを作成 flask_app = Flask(__name__) # Flask アプリを Socket.IO ASGI アプリでラップ app = socketio.ASGIApp(sio, flask_app) @flask_app.route('/') async def index(): return render_template_string(""" <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Flask Async WebSocket Chat</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.0/socket.io.js"></script> <script type="text/javascript" charset="utf-8"> var socket = io(); socket.on('connect', function() { console.log('Connected!'); socket.emit('message', {data: 'I\'m connected async!'}); }); socket.on('my response', function(msg) { document.getElementById('messages').innerHTML += '<p>' + msg.data + '</p>'; }); function sendMessage() { var input = document.getElementById('chatInput'); socket.emit('message', {data: input.value}); input.value = ''; } </script> </head> <body> <h1>Flask Async WebSocket Chat</h1> <input type="text" id="chatInput" onkeydown="if (event.keyCode == 13) sendMessage()"> <button onclick="sendMessage()">Send</button> <div id="messages"></div> </body> </html> """) @sio.on('connect') async def connect(sid, environ): print(f"Client connected: {sid}") await sio.emit('my response', {'data': 'Connected to async server!'}, room=sid) @sio.on('message') async def message(sid, data): print(f"Received message from {sid}: {data['data']}") await sio.emit('my response', data, broadcast=True, skip_sid=sid) # 送信者以外にエコー await sio.emit('my response', {'data': f"You said: {data['data']}"}, room=sid) # 送信者へ確認 @sio.on('disconnect') async def disconnect(sid): print(f"Client disconnected: {sid}") if __name__ == '__main__': # これを実行するには、Uvicorn を使用します: `uvicorn your_app_file:app --reload --port 5000` pass
このセットアップでは、sio = socketio.AsyncServer(async_mode='asgi', ...)
が ASGI に互換性のある非同期 Socket.IO サーバーを作成します。app = socketio.ASGIApp(sio, flask_app)
は Flask アプリケーションを Socket.IO ASGI アプリケーションでラップし、Uvicorn のような単一の ASGI サーバーで HTTP ルートと WebSocket 接続の両方をサービスできるようにします。Socket.IO イベントハンドラは現在 async
関数であり、Flask 2.x の async/await
パラダイムに完全に適合しています。この強力な組み合わせにより、高並行性の Web リクエストとリアルタイム双方向通信の両方を、統一された非同期フレームワーク内で実現できます。
結論
Flask 2.x の async/await
の採用は、開発者がパフォーマンスが重要なタスクや I/O バウンドなタスクに取り組む方法に革命をもたらし、スケーラブルな Web アプリケーションを構築するための堅牢な基盤を提供します。Flask-SocketIO
のような拡張機能や直接の ASGI 統合を介した WebSocket サポートと組み合わされると、Flask は高度にインタラクティブでリアルタイムなユーザーエクスペリエンスの作成を可能にします。非同期ビューと WebSocket を活用することで、開発者は並行処理と永続的な通信を効率的に処理し、ユーザーエンゲージメントと運用効率を向上させる、モダンで応答性の高いアプリケーションを構築できます。Flask の未来は間違いなく非同期であり、リアルタイムです。