Урок по треку Python для веба: Django и FastAPI 7 мин

WSGI и ASGI: как запрос доходит до Python-кода

Как запрос доходит до Python: WSGI и ASGI, gunicorn и uvicorn, синхронные и асинхронные воркеры.

Между браузером и вашей вьюхой стоит несколько слоёв: nginx, сервер приложений (gunicorn, uvicorn), интерфейс WSGI или ASGI и фреймворк. Вопрос «что такое WSGI» задают и джунам, а «почему FastAPI на uvicorn, а Django на gunicorn» уже middle-уровень.

Путь запроса

клиент -> nginx (TLS, статика, буферизация) -> gunicorn/uvicorn (процессы-воркеры)
       -> WSGI/ASGI-приложение (Django, Flask, FastAPI) -> ваш код
  • nginx (обратный прокси) принимает соединения, завершает TLS, отдаёт статику, буферизует медленных клиентов и защищает воркеры.
  • Сервер приложений держит пул воркеров и переводит HTTP в вызов Python-приложения по стандартному интерфейсу.
  • Фреймворк реализует этот интерфейс: маршрутизация, middleware, вьюхи.

WSGI

WSGI (PEP 3333) это синхронный интерфейс: приложение это вызываемый объект, который получает словарь environ с данными запроса и функцию start_response, а возвращает итерируемое тело ответа.

def app(environ, start_response):
    path = environ['PATH_INFO']
    body = f'Привет, {path}'.encode()
    start_response('200 OK', [('Content-Type', 'text/plain; charset=utf-8')])
    return [body]

Один запрос занимает воркер (процесс или поток) от начала до конца. WebSocket и долгие соединения в WSGI не укладываются. На WSGI работают Django (классически) и Flask, серверы: gunicorn, uWSGI.

ASGI

ASGI это асинхронный преемник: приложение это корутина app(scope, receive, send).

async def app(scope, receive, send):
    assert scope['type'] == 'http'
    await send({'type': 'http.response.start', 'status': 200,
                'headers': [(b'content-type', b'text/plain')]})
    await send({'type': 'http.response.body', 'body': b'ok'})
  • scope описывает соединение (тип http, websocket, lifespan), receive и send обмениваются событиями.
  • Поддерживает WebSocket, стриминг, server-sent events и тысячи одновременных соединений в одном процессе.
  • Серверы: uvicorn, hypercorn, granian, daphne. Фреймворки: FastAPI и Starlette, Django (через asgi.py), Litestar.

Воркеры и модели конкурентности

  • gunicorn sync-воркеры: процесс обрабатывает один запрос за раз. Пропускная способность = число воркеров. Медленный внешний вызов блокирует воркер.
  • gthread: несколько потоков в процессе, помогает при I/O.
  • uvicorn (или gunicorn с uvicorn-воркерами): цикл событий в каждом процессе, тысячи одновременных ожиданий, но блокирующий код в async def останавливает весь процесс.
  • Процессов обычно делают по числу ядер или около 2 * CPU + 1 для sync-модели, потом подбирают нагрузочным тестом.

Django: WSGI или ASGI

В проекте Django есть оба файла: wsgi.py и asgi.py. Асинхронные вьюхи Django работают и под WSGI, но тогда каждый запрос всё равно занимает воркер, и выгоды нет. Чтобы получить пользу от async def, Django запускают под ASGI-сервером. ORM Django при этом в основном синхронный внутри: async-методы (aget, acount и т.п.) выполняют запросы через поток.

Частые ошибки

  • Запуск через manage.py runserver или uvicorn --reload в проде.
  • Отсутствие таймаутов: воркер висит на медленном клиенте или внешнем API.
  • Синхронные блокирующие вызовы внутри async-обработчиков.
  • Мысль, что ASGI сам по себе ускоряет CPU-bound код.

Что спросят на собесе

  • Что такое WSGI, как выглядит минимальное WSGI-приложение.
  • Чем ASGI отличается от WSGI, зачем он появился.
  • Зачем nginx перед gunicorn или uvicorn.
  • Как работают воркеры gunicorn, сколько их ставить.
  • Почему FastAPI запускают на uvicorn, а Django может работать и так, и так.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.