В FastAPI-приложениях часто используются синхронные библиотеки, которые могут блокировать event loop, если не вынесены в отдельные потоки. Вот список самых распространённых:
| Библиотека | Назначение | Асинхронный аналог |
|---|---|---|
psycopg2 |
PostgreSQL | asyncpg, sqlalchemy.ext.asyncio |
mysql-connector-python |
MySQL | aiomysql |
sqlite3 |
SQLite | aiosqlite |
SQLAlchemy (синхронный режим) |
ORM | SQLAlchemy 2.0+ (async) |
pymongo |
MongoDB | motor |
Пример проблемы:
# Синхронный запрос к PostgreSQL (блокирует loop, даже в `def`-маршруте!)
from sqlalchemy import create_engine
engine = create_engine("postgresql://user:pass@localhost/db")
@app.get("/sync-db")
def sync_db():
with engine.connect() as conn:
result = conn.execute("SELECT * FROM users") # Блокирующий вызов
return {"data": result.fetchall()}| Библиотека | Асинхронный аналог |
|---|---|
requests |
httpx.AsyncClient, aiohttp |
urllib3 |
Встроен в httpx |
Пример:
import requests
@app.get("/sync-http")
def sync_http():
r = requests.get("https://api.example.com") # Блокирует поток
return r.json()| Библиотека | Асинхронный аналог |
|---|---|
Встроенный open() |
aiofiles |
pandas.read_csv()/to_csv() |
polars (частично async) |
json.load()/dump() |
aiofiles + json |
Пример:
@app.get("/read-file")
def read_file():
with open("large_file.csv") as f: # Блокирующая операция
data = f.read()
return {"size": len(data)}| Библиотека | Проблема | Решение |
|---|---|---|
numpy |
CPU-bound | ProcessPoolExecutor |
pandas |
CPU-bound + GIL | То же |
scikit-learn |
CPU-bound | Вынос в отдельные процессы |
Пример:
import pandas as pd
@app.get("/process-data")
def process_data():
df = pd.read_csv("data.csv") # Загружает в потоке
result = df.mean().to_dict() # CPU-операция
return result| Библиотека/Модуль | Асинхронная альтернатива |
|---|---|
time.sleep() |
asyncio.sleep() |
subprocess.run() |
asyncio.create_subprocess_exec |
os.walk() |
aiofiles.os.walk() (через сторонние решения) |
Пример:
import time
@app.get("/delay")
def delay():
time.sleep(5) # Блокирует поток
return {"message": "Готово"}- Авторизация:
passlib,bcrypt→ Асинхронных аналогов нет (выносить в потоки). - Кэширование:
redis-py→aioredis. - Шаблонизация:
Jinja2→jinja2.Environment(но обычно быстрая, не критично).
- В
async def-маршрутах синхронные библиотеки полностью блокируют event loop. - В
def-маршрутах они работают в потоках, но:- При высокой нагрузке пул потоков исчерпывается.
- CPU-операции страдают от GIL.
-
Для
def-маршрутов:- Увеличить пул потоков (если I/O-bound):
import concurrent.futures pool = concurrent.futures.ThreadPoolExecutor(max_workers=100)
- Для CPU-bound:
ProcessPoolExecutor.
- Увеличить пул потоков (если I/O-bound):
-
Для
async def-маршрутов:- Всегда использовать
asyncio.to_thread():await asyncio.to_thread(requests.get, "https://api.example.com")
- Или переходить на async-аналоги (
httpx,asyncpg).
- Всегда использовать
-
Глобальное решение:
Постепенная замена синхронных библиотек на асинхронные.
| Тип операции | Синхронная библиотека | Стратегия оптимизации |
|---|---|---|
| I/O-bound (БД, HTTP) | psycopg2, requests |
Пул потоков или async-аналоги |
| CPU-bound (математика) | numpy, pandas |
ProcessPoolExecutor |
| Файлы | open(), pandas.read_csv() |
aiofiles, потоки |
| Утилиты | time.sleep() |
asyncio.sleep() |
Главное правило: Всегда проверяйте документацию библиотеки на предмет async-поддержки. Если её нет — планируйте вынос в потоки или процессы.