← Назад к вопросам

Асинхронен ли 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 — это стандарт де-факто.

Асинхронен ли wsgi | PrepBro