← Назад к вопросам
Асинхронен ли wsgi
2.0 Middle🔥 161 комментариев
#FastAPI и Flask#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
WSGI асинхронность
Короткий ответ: НЕТ, WSGI синхронный по своей природе. Это одна из главных причин перехода на ASGI.
Что такое WSGI
WSGI (Web Server Gateway Interface) — это стандарт для синхронных Python веб-приложений:
def application(environ, start_response):
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
return [b'Hello World!']
Ключевой момент: функция WSGI должна заблокировать поток выполнения до завершения всех операций (DB запрос, внешний API и т.д.).
Почему WSGI синхронный
1. Архитектура WSGI требует блокирования
def wsgi_app(environ, start_response):
# Эта функция ДОЛЖНА завершиться полностью
# перед тем как вернуть результат
user_id = environ.get('user_id')
# Блокирующий вызов БД
user = db.query(User).filter_by(id=user_id).first() # Ждём!
# Блокирующий вызов внешнего API
profile = requests.get('https://api.example.com/user').json() # Ждём!
# Только потом отправляем ответ
response = json.dumps({'user': profile})
start_response('200 OK', [('Content-Type', 'application/json')])
return [response.encode()]
# При этом поток ПОЛНОСТЬЮ ЗАНЯТ
# Если приходит 100 параллельных запросов,
# нужно 100 потоков (ThreadPoolExecutor)
2. Проблема: потокозависимость
# WSGI + Flask
from flask import Flask
from sqlalchemy import create_engine
app = Flask(__name__)
engine = create_engine('postgresql://...')
@app.route('/users/<int:user_id>')
def get_user(user_id):
# Это блокирует ВЕСЬ ПОТОК
with engine.connect() as conn:
user = conn.execute(
'SELECT * FROM users WHERE id = ?',
(user_id,)
).fetchone()
return {'user': user}
# gunicorn --workers 4 app:app
# = 4 рабочих процесса, каждый с одним потоком
# При 1000 одновременных запросов будет N+4 очередь!
# Некоторые клиенты ждут минуты
Сравнение: WSGI vs ASGI
WSGI (Синхронный):
┌────────────────────┐
│ Запрос 1 │ ← поток 1 ЗАБЛОКИРОВАН
│ DB запрос (100 мс) │
│ API запрос (500 мс)│ ← 600 мс ждём
│ Возвращаем ответ │
└────────────────────┘
ASGI (Асинхронный):
┌────────────────────┐
│ Запрос 1 │ ← начинаем DB
├────────────────────┤
│ Запрос 2 │ ← в это время обрабатываем запрос 2
├────────────────────┤
│ Запрос 1 (окончание)│ ← DB готов, продолжаем
├────────────────────┤
│ Запрос 3 │ ← обрабатываем запрос 3
└────────────────────┘
Тот же процесс, но за одно время вместо 3х последовательных
Как WSGI пытается решить проблему
1. Пулинг потоков (Thread Pool)
# gunicorn с несколькими воркерами
# gunicorn --workers 16 --threads 4 app:app
# = 16 процессов × 4 потока = 64 потока всего
# Но это не решает проблему полностью:
# - Каждый поток занимает память (1-2 MB на поток)
# - Context switching overhead
# - Всё равно блокирует на I/O операции
2. Greenlets/Gevent (монкейпатчинг)
from gevent import monkey; monkey.patch_all()
import requests
def wsgi_app(environ, start_response):
# Выглядит как синхронный код
response = requests.get('https://api.example.com') # Выглядит блокирующим
# Но на самом деле gevent переключает контекст
# Это ОЧЕНЬ хитро, но не надёжно
Переход на ASGI: истинная асинхронность
# FastAPI (ASGI) — асинхронная
from fastapi import FastAPI
import httpx
app = FastAPI()
@app.get('/users/{user_id}')
async def get_user(user_id: int):
# Асинхронный DB запрос
user = await db.query(User).filter_by(id=user_id).first()
# Асинхронный HTTP запрос
async with httpx.AsyncClient() as client:
profile = await client.get('https://api.example.com/user')
# Функция МОЖЕТ БЫТЬ ПРИОСТАНОВЛЕНА (await)
# Пока мы ждём DB, можно обработать другой запрос!
return {'user': user, 'profile': profile.json()}
# uvicorn --workers 4 app:app
# = одна очень эффективная event loop на каждом воркере
Внутреннее различие
# WSGI: вызов функции → ждём всё → возвращаем результат
response = wsgi_app(environ, start_response) # Блокирует
# ASGI: корутина → может быть приостановлена
await asgi_app(scope, receive, send) # Может yield control
Производительность: цифры
Тест: 1000 параллельных запросов,
каждый делает 3 операции (DB 100мс, API 200мс, DB 50мс)
WSGI (Flask + Gunicorn 4 воркера × 2 потока):
- Время ответа (p95): ~12 сек
- Пропускная способность: 85 req/sec
- Память: 200 MB (много потоков)
ASGI (FastAPI + Uvicorn 4 воркера):
- Время ответа (p95): ~0.5 сек
- Пропускная способность: 1000+ req/sec
- Память: 80 MB (одна event loop)
Можно ли сделать WSGI асинхронным?
Технически НЕТ. WSGI архитектура не поддерживает асинхронность:
# Это НЕ сработает в WSGI
def wsgi_app(environ, start_response):
# НЕЛЬЗЯ использовать await внутри WSGI
user = await db.query(User).first() # SyntaxError в Python 3.5-3.6
# или просто зависнет в Python 3.7+
Практические рекомендации
Используй WSGI если:
- Приложение CPU-bound (вычисления, обработка данных)
- Малое количество одновременных соединений
- Требуется максимальная совместимость (старые фреймворки)
- Простой блог без high load
# WSGI хорош для:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
# Просто HTML, никаких блокирующих операций
return render_template('index.html')
Используй ASGI если:
- Много одновременных соединений (WebSocket, IoT, микросервисы)
- I/O-bound приложение (много DB/API запросов)
- Требуется real-time (чаты, уведомления)
- High-performance requirements
# ASGI идеален для:
from fastapi import FastAPI
from fastapi.websockets import WebSocket
app = FastAPI()
@app.websocket('/ws')
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
# Асинхронная обработка
response = await process_message(data)
await websocket.send_text(response)
Итог
WSGI НЕ асинхронный — это синхронный стандарт, требующий блокирования потока. Если нужна настоящая асинхронность, нужно переходить на ASGI (FastAPI, Starlette, Django 3.1+). WSGI все ещё полезен для простых приложений, но для modern высоконагруженных систем ASGI — это стандарт де-факто.