本番環境での非同期 I/O パターン
HTTP クライアント、キュー、実際のパイプラインに asyncio を適用します。
「本番環境での非同期 I/O パターン」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。
イベントループのベストプラクティス
常にasyncio.run()を唯一のエントリーポイントとして使用します。同期コードと非同期コードを不用意に混在させないでください。また、時間のかかるCPU処理でイベントループを決してブロックしないでください。
import asyncio
async def main():
# Never: time.sleep() — blocks the event loop
# Always: await asyncio.sleep()
await asyncio.sleep(1)
asyncio.run(main())接続プール
リクエストごとに新しい接続を開くのではなく、接続を再利用します。asyncpgやaiohttpなどのライブラリには、組み込みの接続プールが用意されています。
# pip install asyncpg
import asyncpg, asyncio
async def main():
pool = await asyncpg.create_pool(
dsn="postgresql://user:pass@host/db",
min_size=5, max_size=20
)
async with pool.acquire() as conn:
row = await conn.fetchrow("SELECT 1")
await pool.close()asyncio.Queueによるプロデューサー・コンシューマー
asyncio.Queueを使用すると、並行して実行されるプロデューサーとコンシューマーのタスクを分離できます。
import asyncio
async def producer(q):
for i in range(5):
await q.put(i)
await asyncio.sleep(0.5)
await q.put(None) # sentinel
async def consumer(q):
while (item := await q.get()) is not None:
print(f"processing {item}")
q.task_done()
async def main():
q = asyncio.Queue()
await asyncio.gather(producer(q), consumer(q))
asyncio.run(main())セマフォによるレート制限
外部APIに過剰な負荷をかけたり、レート制限に達したりしないよう、外向きのリクエスト数を制限します。
import asyncio, aiohttp
SEM = asyncio.Semaphore(10) # 10 concurrent requests max
async def fetch(session, url):
async with SEM:
async with session.get(url) as r:
return await r.json()
async def main(urls):
async with aiohttp.ClientSession() as s:
return await asyncio.gather(*[fetch(s, u) for u in urls])指数バックオフによる再試行
一時的な障害に適切に対処するため、再試行可能な処理をバックオフのロジックでラップします。
import asyncio
async def retry(coro_fn, retries=3, base=1):
for attempt in range(retries):
try:
return await coro_fn()
except Exception as e:
if attempt == retries - 1: raise
delay = base * 2 ** attempt
await asyncio.sleep(delay)グレースフルシャットダウン
長時間実行されるタスクではasyncio.CancelledErrorを捕捉し、OSシグナルを処理して正常にシャットダウンします。
import asyncio, signal
async def server():
stop = asyncio.Event()
loop = asyncio.get_running_loop()
loop.add_signal_handler(signal.SIGINT, stop.set)
await stop.wait()
print("Shutting down gracefully")ストリーミングレスポンス
非同期ジェネレーターを使用すると、大きなレスポンス全体をメモリに読み込まずにストリーミングできます。
import asyncio, aiohttp
async def stream(url):
async with aiohttp.ClientSession() as s:
async with s.get(url) as r:
async for chunk in r.content.iter_chunked(4096):
process(chunk)
async def process(chunk):
await asyncio.sleep(0)
print(len(chunk), "bytes")FastAPIのバックグラウンドタスク
FastAPIのBackgroundTasksは、レスポンスの送信後に実行する処理をキューに追加するため、エンドポイントの応答性を維持できます。
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def send_email(to: str, body: str):
# heavy synchronous work
pass
@app.post("/register")
async def register(email: str, bg: BackgroundTasks):
bg.add_task(send_email, email, "Welcome!")
return {"status": "registered"}ハートビートパターン
create_taskを使用して、メインのサーバーループと並行して定期的なバックグラウンドタスクを実行します。
import asyncio
async def heartbeat():
while True:
print("alive")
await asyncio.sleep(30)
async def main():
asyncio.create_task(heartbeat())
await serve_forever()非同期コードのプロファイリング
非同期処理に対応したプロファイリングには、yappiまたはpy-spyを使用します。asyncioのデバッグモードを有効にすると、遅いコールバックに関する警告を確認できます。
# Enable debug to catch slow callbacks:
# asyncio.run(main(), debug=True)
# PYTHONASYNCIODEBUG=1 python server.py
# logs: Executing <Task coroutine=...> took 0.150 secondsよくある落とし穴の回避
重要なルールは次のとおりです。イベントループ上でブロッキング処理を呼び出さないこと、コルーチンを必ずawaitすること、シャットダウン時にタスクをキャンセルすること、接続プールを使用すること、セマフォで同時実行数を制限することです。
import asyncio
async def bad():
import time
time.sleep(2) # blocks the entire event loop!
async def good():
await asyncio.sleep(2) # yields control確認問題
並行して実行されるプロデューサーとコンシューマーを分離するasyncioのプリミティブはどれですか。
まとめ
本番環境での非同期処理パターンでは、接続プールを使用し、Semaphoreで同時実行数を制御し、再試行とバックオフを実装し、大きなデータをストリーミングし、シグナル受信時に正常にシャットダウンし、create_taskで定期的なタスクを実行します。
よくある質問
「本番環境での非同期 I/O パターン」レッスンは無料ですか?
はい。「本番環境での非同期 I/O パターン」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。
「本番環境での非同期 I/O パターン」で何を学びますか?
HTTP クライアント、キュー、実際のパイプラインに asyncio を適用します。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Python Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「本番環境での非同期 I/O パターン」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このPython Academyレッスンでコードを書いて実行できますか?
はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- コルーチンとイベントループ
- await、タスク、ギャザリング
- 非同期コンテキストマネージャーとイテレーター
- 本番環境での非同期 I/O パターン