Любому веб-приложению нужен веб-сервер для доступа извне. На самом деле, многие даже не уделяют выбору веб-сервера для своего приложения достаточного внимания: на Django берут "популярный" Gunicorn (а кто-то ещё и обвязывает его Uvicorn'ом), а для FastAPI Uvicorn практически стандарт. Тут "на сцену" выходит Granian, представляющий впечатляющие результаты производительности с простой настройкой.
В своём проекте "Код на салфетке" я использовал uWSGI, но недавно узнал про новый Granian и мне стало интересно на что он способен. Так появилась идея для этой статьи.
В этой статье узнаем:
Что такое веб-сервер и какие у них есть интерфейсы взаимодействия.
Какие есть популярные WSGI и ASGI веб-серверы.
Сравним их производительность.
Если вам интересны подобные материалы, подписывайтесь на наш Telegram-канал «Код на салфетке». У нас много интересного, в комментариях можно обсудить или предложить идею для статьи.
Что такое веб-сервер
Веб-сервер, в рамках применения в Python, это программа, которая запускает Python-приложение и обеспечивает его взаимодействие с внешним миром по протоколу HTTP (или другим, если необходимо, например, WebSocket).
Веб-сервер принимает внешние запросы, передаёт их в приложение через интерфейс (WSGI, ASGI или RSGI), а затем возвращает ответ. Проще говоря, веб-сервер — это связующее звено между кодом и пользователем.
Воркеры и процессы — это ключевые понятия для понимания работы веб-серверов:
Воркер (worker) — это отдельный процесс или поток, который обрабатывает входящие запросы.
Процесс — это независимая программа в операционной системе со своей памятью.
Использование нескольких воркеров позволяет обрабатывать запросы параллельно, что увеличивает производительность.
Интерфейсы взаимодействия
Для "общения" между веб-сервером и приложением существует три интерфейса взаимодействия:
WSGI (Web Server Gateway Interface).
ASGI (Asynchronous Server Gateway Interface).
RSGI (Rust Server Gateway Interface).
Разберём подробнее каждый.
WSGI (Web Server Gateway Interface)
Стандарт интерфейса между Python-приложением и веб-сервером, появившийся ещё в Python 2.2 (PEP 333, потом обновлён до PEP 3333).
Синхронный интерфейс взаимодействия — обрабатывает запросы один за другим.
Применяется преимущественно в "классических" фреймворках, вроде Django или Flask.
Не поддерживает WebSocket, SSE (Server-Sent Events) и некоторые другие возможности.
Достаточно простой интерфейс, подходящий для небольших и низконагруженных приложений.
ASGI (Asynchronous Server Gateway Interface)
Современный стандарт интерфейса для Python-приложений. Придуман как "духовный наследник" WSGI.
Синхронный и асинхронный интерфейс взаимодействия — может обрабатывать множество запросов одновременно.
Применяется в "молодых" фреймворках, вроде FastAPI, а также может применяться в Django (через Channels) или Flask.
Поддерживает WebSocket, SSE, HTTP/2 и так далее.
Актуальный интерфейс взаимодействия для больших и высоконагруженных приложений.
RSGI (Rust Server Gateway Interface)
Новый проект (2024-2025), вдохновлённый WSGI/ASGI, но созданный с прицелом на Rust + Python экосистему. Его продвигают разработчики Granian и Uvicorn.
Идея: минималистичный интерфейс между веб-сервером и приложением.
Оптимизирован для Rust-бэкендов и Python-приложений, чтобы убрать избыточные прослойки.
Цель — заменить ASGI/WSGI в будущих фреймворках.
Поддерживает синхронные, асинхронные и нативные Rust-хендлеры.
Акцент на высокую производительность и простоту (ASGI со временем оброс сложностями).
Экспериментальный интерфейс, появившийся совсем недавно. Может проявить себя в высоконагруженных приложениях, написанных на Python, или комбинированных системах, использующих не только Python, но и Rust.
Python веб-серверы
Веб-серверов для Python много, но мы разберём и сравним только несколько — те, которые чаще всего встречаются в обучающих статьях, а также рассмотрим на их фоне Granian.
Обратите внимание! Производительность и потребление ресурсов собраны из разных источников и чаще всего описывают "Hello World" тесты. Значения усреднены и могут отличаться на реальных проектах, поскольку на производительность влияет не только сам веб-сервер, но и оптимизация приложения.
Gunicorn (WSGI)
Лёгкий WSGI-сервер для Python (Green Unicorn). Широко используется с Django/Flask. Работает по схеме prefork (несколько процессов-воркеров).
Конфигурация: простая. Например,
gunicorn --workers 4 myproject.wsgi:application
. Хорошо масштабируется на нескольких ядрах. Формула расчета воркеров: рекомендуется число воркеров ≈ (2 × количество CPU ядер) + 1.Особенности: реализует только WSGI, не поддерживает ASGI/async "из коробки". Для работы с асинхронными задачами можно применять сторонние классы воркеров (gevent/eventlet) или использовать
gunicorn
+uvicorn
-воркеры. Не имеет встроенной поддержки WebSocket (только HTTP).Производительность: хорошие показатели для синхронных нагрузок. ~3 000-10 000 запросов/сек — в зависимости от числа воркеров, окружения, нагрузки. При этом память на воркер ~30 MB.
Установка:
pip install gunicorn
Запуск (Django):
gunicorn --workers 4 myproject.wsgi:application
Запуск (Flask):
gunicorn --workers 4 "myapp:app"
uWSGI (WSGI)
"Полноценный" веб-сервер/приложение-сервер. Поддерживает WSGI, FastCGI, HTTP и др. Можно использовать в режиме префорк или с потоками/Greenlets.
Конфигурация: очень гибкая и сложная. Требует конфигурационного файла (uwsgi.ini) с множеством опций (процессы, потоки, кеширование, балансировка). Из-за богатого набора опций порог вхождения высок.
Особенности: предназначен для высоких нагрузок и масштабируемых систем (например, Emperor mode, multiple apps). Поддерживает горячую замену кода, кэширование и балансировку процессов.
Производительность: несколько лучше, чем у Gunicorn в бенчмарках: 10-12 тысяч запросов/сек. Память на воркер чуть выше — 40 MB.
Установка:
pip install uwsgi
Запуск (Django):
uwsgi --http :8000 --module myproject.wsgi:application --processes 4 --threads 2
Запуск (Flask):
uwsgi --http :8000 --module myapp:app --callable app
Пример uwsgi.ini:
[uwsgi]
module = myproject.wsgi:application
master = true
processes = 4
threads = 2
http = :8000
Uvicorn (ASGI)
Лёгкий ASGI-сервер для Python. Построен на основе uvloop и httptools (Cython). Предназначен для асинхронных приложений (Starlette, FastAPI, Django Channels).
Конфигурация: очень простая. Запускается одной командой:
uvicorn myapp.asgi:application --workers 4
. Не имеет сложных конфигов, можно задавать параметры в CLI или скрипте Python.Особенности: поддерживает HTTP/1.1, HTTP/2, WebSocket. Имеет низкий оверхед и малый размер кода, использует готовые асинхронные event loop (uvloop).
Производительность: высокая: ~35 000-40 000 запросов/сек. Низкая задержка. Память на воркер примерно 20 MB.
Установка:
pip install uvicorn[standard]
Запуск (FastAPI):
uvicorn main:app --workers 4 --reload
Запуск (Django Channels):
uvicorn myproject.asgi:application --workers 4
Granian (WSGI, ASGI, RSGI, Rust)
Новый HTTP-сервер для Python-приложений, написанный на Rust (использует Hyper/Tokio). Поддерживает сразу WSGI, ASGI и RSGI-интерфейсы.
Конфигурация: простой CLI или кодовый API. Например,
granian --interface asgi myapp:app
для запуска ASGI-приложения. Можно задавать количество процессов/потоков.Особенности: ориентирован на максимальную производительность и пропускную способность. Из коробки поддерживает HTTP/1, HTTP/2, WebSocket'ы, HTTPS, статические файлы. Можно конфигурировать SSL/mTLS, расширения ASGI. Лёгкий (нет проблем с GIL, может использовать Rust-потоки).
Производительность: очень высокая: ~40 000-45 000 запросов/сек в ASGI-режиме и ~40 000 запросов/сек в WSGI-режиме. Максимальная пропускная способность. Память на процесс ~15 MB.
GIL (Global Interpreter Lock) — это механизм в Python, который позволяет выполнять только один поток Python-кода одновременно. Это ограничение влияет на производительность многопоточных приложений, но не касается многопроцессных решений.
Установка:
pip install granian
Запуск (FastAPI / ASGI):
granian --interface asgi main:app --workers 4
Запуск (Django WSGI):
granian --interface wsgi myproject.wsgi:application --workers 4
Запуск (Django Channels):
granian --interface asgi myproject.asgi:application --workers 4
Запуск (FastAPI / RSGI — экспериментально):
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello from RSGI + FastAPI"}
Команда запуска:
granian --interface rsgi main:app --workers 4
Важно: RSGI не поддерживается существующими фреймворками вроде Django или Flask, поэтому применять его можно только с библиотеками/фреймворками, изначально рассчитанными на асинхронный стек. Для FastAPI RSGI может дать буст, но для Django он неприменим (на момент написания статьи).
Сравнение производительности
RPS и Latency — важные метрики производительности:
RPS (Requests Per Second) — количество запросов, которые сервер может обработать за секунду.
Latency (задержка) — время, которое требуется для обработки одного запроса.
RPS (запросов в секунду):

Gunicorn: ~3 000–10 000 запросов/сек.
uWSGI: 10 000–12 000 запросов/сек.
Uvicorn: ~35 000–40 000 запросов/сек.
-
Granian:
ASGI: ~40 000–45 000 запросов/сек.
WSGI: ~40 000 запросов/сек.
Вывод по производительности: ASGI-серверы (Uvicorn, Granian) показывают значительно лучшие результаты по количеству обрабатываемых запросов в секунду по сравнению с традиционными WSGI-серверами. Granian демонстрирует лучшие показания благодаря оптимизациям на Rust и отсутствию ограничений GIL.
Потребление памяти:

Gunicorn: ~30 MB на воркер.
uWSGI: ~40 MB на воркер.
Uvicorn: ~20 MB на воркер.
Granian: ~15 MB на воркер.
Вывод по памяти: Granian показывает наименьшее потребление памяти благодаря эффективной реализации на Rust. Uvicorn также демонстрирует хорошие показатели. Традиционные WSGI-серверы потребляют больше памяти из-за особенностей архитектуры Python и дополнительных слоев абстракции.
Задержка (Latency):

Gunicorn: ~6-12 мс (средняя задержка).
uWSGI: ~4-8 мс (средняя задержка).
Uvicorn: ~2-5 мс (средняя задержка).
-
Granian:
ASGI: ~0.7-1.2 мс (средняя задержка).
WSGI: ~1.5-2.5 мс (средняя задержка).
Вывод по задержкам: Granian демонстрирует исключительно низкую задержку во всех режимах работы. ASGI-реализация показывает субмиллисекундные задержки, что критично для высокочувствительных к латентности приложений. Даже в WSGI-режиме Granian превосходит традиционные серверы по этой метрике.
Пара слов о Granian
Приложения на Rust всё чаще проникают в закулисья Python-библиотек. Первой ласточкой был "нашумевший" пакетный менеджер uv
, обеспечивший высокую скорость работы с окружением проекта. Теперь о себе заявляет Granian как новый и, что немаловажно, быстрый веб-сервер.
Ради интереса, перед написанием статьи я решил попробовать его в работе с Django.
Как я упомянул во вступлении, на сайте использовался uWSGI — он неплох, но его главный минус в сложности настройки для новичков. Запуск веб-сервера выглядел так:
command: ["uwsgi", "--ini", "/code/uwsgi.ini"]
Замена на Granian не вызвала вообще никаких проблем, поскольку ему не нужны конфиг-файлы и он запускается одной командой:
command: [
"granian",
"--interface", "wsgi",
"--host", "0.0.0.0",
"--port", "8000",
"--workers", "2",
"--backpressure", "30",
"--workers-lifetime", "3600",
"--log-level", "info",
"pressanybutton.wsgi:application"
]
Настройка параметров Granian
Количество воркеров рассчитывается по той же формуле: (2 × количество CPU ядер) + 1. Для контейнера с 1 CPU достаточно 2-3 воркеров.
Backpressure — это механизм контроля нагрузки. Параметр --backpressure 30
означает, что если в очереди накопилось 30 запросов, сервер начнёт отклонять новые подключения. Это защищает от перегрузки. Для стандартных лимитов PostgreSQL в 100 одновременных подключений, backpressure 30 — разумное значение, оставляющее запас для других процессов.
Workers-lifetime — время жизни воркера в секундах. Через 3600 секунд (1 час) воркер перезапускается, что помогает избежать утечек памяти.
Заключение
Безусловно, в статье упомянуты далеко не все существующие веб-серверы для Python-проектов. Я старался брать те, которые "на слуху", то есть фигурируют в гайдах или книгах. Несмотря на это, получившееся сравнение наглядно демонстрирует разницу в производительности между классическими синхронными (WSGI) и современными асинхронными (ASGI) подходами. Особенно выделяется Granian, предоставляющий в WSGI-режиме схожую с ASGI производительность.
Основные выводы:
ASGI-серверы значительно превосходят WSGI по производительности
Granian показывает лучшие результаты как по скорости, так и по потреблению памяти
Выбор сервера зависит от требований проекта: для простых приложений достаточно Gunicorn, для высоконагруженных — Uvicorn или Granian
Rust-решения в Python-экосистеме становятся трендом, обеспечивая лучшую производительность
Напишите в комментарии ваши мысли по поводу увеличения роли Rust-приложений в Python-библиотеках, а также каким веб-сервером пользуетесь вы! Интересно будет почитать.
Подписывайтесь на наш Telegram-канал «Код на салфетке» — у нас много материалов для новичков по Python и DevOps, а также интересные проекты!
Комментарии (0)
Arduinum
18.09.2025 11:10Хорошо, что Python веб-приложения становятся быстрее. Конечно до Drogon на C++ ещё далеко, но это Python. Ну и конечно Drogon это фреймворк с веб-сервером, а не отдельный веб-сервер. Пока нагрузка небольшая на сервер, то по большому счёту нет сильной разницы какой асинхронный веб-фреймворк использовать. Разница начинается на больших нагрузках.
baldr
18.09.2025 11:10Ради интереса, перед написанием статьи я решил попробовать его в работе с Django.
Так и чем всё закончилось? Стало быстрее? А что быстрее?
Не очень понятно что должно измениться в случае WSGI-приложения. Шаблоны рендериться будут с такой же скоростью, да и интерпретатор питона будет тот же самый.
proDream Автор
18.09.2025 11:10Улучшился отклик сайта, и в перспективе он будет обрабатывать большое количество запросов лучше и стабильнее чем uWSGI
baldr
18.09.2025 11:10Простите, но это ответ из серии "дерево справа более зелёное, чем слева".
Что значит "улучшился"? За счёт чего? У вас в обоих случаях за RSGI/WSGI фронтендом стоит питоновское приложение, одно и то же. И интерпретатор один и тот же. Однопоточный. В случае gunicorn фактически воркер и приложение работают в одном процессе. В RSGI, как я понимаю, бинарник на Rust вызывает Python-код, это уже некоторые накладные расходы. В любом случае, 98% времени будет выполняться именно Python-код джанги и приложения, а здесь выигрыша никакого не предвидится.
Разница WSGI и ASGI хотя бы понятна из-за чего. Здесь хотелось бы либо объяснения, либо результатов с тестами.
Опять же, перед всем этим в любом случае будет стоять балансер типа nginx, который возьмёт на себя основные расходы типа терминирования TLS и медленных клиентов.
proDream Автор
18.09.2025 11:10RSGI не применяется в джанге, это отмечено в статье. Granian работает по WSGI, однако, даже в работе синхронно он обеспечивает производительность сопоставимую с ASGI. После реверс прокси идёт именно веб-сервер, у него есть свои задержки выполнения, это сравнение также есть в статье. Если брать значения "в лоб", у granian в шесть раз лучше показать задержки выполнения, а затем уже да, идёт джанга. И вот этого достаточно, чтобы повысить отзывчивость сайта. Преимущества в RPS проявят себя со временем при росте посещаемости. Gunicorn'а у меня никогда не было, я сразу использовал uWSGI и заменил его на Granian, вместо обвзяки Django Cannels + uvicorn.
Arduinum
18.09.2025 11:10Было-бы интересно увидеть статью как автор встраивает RSGI в своё веб-приложение, посмотреть нагрузку в его приложении и услышать его личный отзыв от использования.
proDream Автор
18.09.2025 11:10Думаю, что опробую его в связке с FastAPI и изучу тему тестирования нагрузочного, чтобы примерно представлять производительность
andreymal
18.09.2025 11:10Синхронный интерфейс взаимодействия — обрабатывает запросы один за другим.
Из первого не следует второе — к WSGI вполне прикручивают потоки или гринлеты (о чём вы сами же потом пишете ниже)
Не поддерживает ... SSE (Server-Sent Events)
SSE — это всего лишь один из вариантов streaming response, который прекрасно делается через WSGI (другое дело, что он будет занимать собой воркер, поэтому делать так обычно не очень разумно)
Синхронный и асинхронный интерфейс взаимодействия — может обрабатывать множество запросов одновременно.
Из первого не следует второе — стандартный event loop в питоне принципиально однопоточный, поэтому, если не обмазываться костылями вроде воркеров или стороннего пула потоков, запросы будут обрабатываться конкурентно, но не одновременно
может применяться в Django (через Channels)
Django уже давно поддерживает ASGI нативно без Channels (единственный мой пост как раз об этом)
Работает по схеме prefork (несколько процессов-воркеров).
То есть для uWSGI вы гринлеты упомянули, а gunicorn, у которого буквально слово green в названии, почему-то решили обидеть
сторонние классы воркеров (gevent/eventlet)
Они не сторонние
Производительность: несколько лучше, чем у Gunicorn в бенчмарках
А вот этот бенчмарк говорит, что uWSGI самый медленный (впрочем, насколько можно верить бенчмарку девятилетней давности, не знаю — но мне до сих пор интересно, что не так с этим бенчмарком)
proDream Автор
18.09.2025 11:10А вот этот бенчмарк говорит, что uWSGI самый медленный (впрочем, насколько можно верить бенчмарку девятилетней давности, не знаю — но мне до сих пор интересно, что не так с этим бенчмарком)
Tishka17
18.09.2025 11:10granian - это конечно прикольно, но если с uvicorn вы можете запустить его из асинк функции (проведя нужную инициализацию) с помощью server.serve(). То с гранианом так уже не получится, он сам гоняет свой луп и никак иначе (по крайней мере среди стабильного апи)
RSGI в целом протокол странный. Описание очень скудное и такое ощущение что никто не задумывался об альтернативных реализациях.
proDream Автор
18.09.2025 11:10Granian также можно вызывать из кода:
https://github.com/emmett-framework/granian?tab=readme-ov-file#embedding-granian-in-your-project
Про RSGI не могу ничего сказать. Он ещё очень молод, поэтому и скуден. Интересно будет наблюдать за его развитием, как со стороны применения в Python, так и в самом Rust
Gorushka
Скоро всем питоном на раст переедем )))))
proDream Автор
Ну на раст прям не факт, но вот то, что библиотеки используют его под капотом для ускорения мне определённо нравится