Почему не рекомендуют использовать wsgiref в Python?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Почему не рекомендуют использовать wsgiref
Введение
wsgiref — это встроенный в Python модуль, предоставляющий простой WSGI сервер (WSGIServer). Хотя это выглядит удобно, его НЕ рекомендуют использовать в production, и даже для разработки есть лучшие альтернативы.
Основные проблемы wsgiref
1. Однопоточность (Single-threaded)
wsgiref обрабатывает запросы последовательно, один за другим:
# wsgiref
from wsgiref.simple_server import make_server
def app(environ, start_response):
print("Начало обработки запроса")
import time
time.sleep(5) # Имитация долгой операции
print("Запрос обработан")
start_response('200 OK', [('Content-Type', 'text/plain')])
return [b'Hello']
# Если два клиента подключатся одновременно:
# Клиент 1: начало (0s) → конец (5s)
# Клиент 2: начало (5s) → конец (10s)
# Второй клиент ждёт 5 секунд, пока первый завершится!
server = make_server('localhost', 8000, app)
server.serve_forever()
Сравни с Gunicorn или другим production сервером:
# Gunicorn (многопроцессный)
# Запускается с 4+ workers'ами по умолчанию
# Клиент 1: обработка на worker 1 (0-5s)
# Клиент 2: обработка на worker 2 (0-5s) одновременно
# Оба завершаются за 5 секунд!
2. Проблема с блокированием
Если приложение делает I/O операции, весь сервер блокируется:
import requests
from wsgiref.simple_server import make_server
def app(environ, start_response):
# Долгий HTTP запрос к внешнему API
response = requests.get('https://api.example.com/data', timeout=30)
start_response('200 OK', [('Content-Type', 'application/json')])
return [response.content]
# Во время requests.get() сервер не может обрабатывать другие запросы!
# Все остальные клиенты ждут 30 секунд
server = make_server('localhost', 8000, app)
server.serve_forever()
С production сервером это не проблема:
# Gunicorn с 4 workers'ами может обрабатывать 4+ таких запроса одновременно
# Каждый worker блокируется независимо
3. Отсутствие масштабируемости
wsgiref не может масштабироваться:
# wsgiref
# Максимум 1 запрос в секунду (если каждый обрабатывается 1 сек)
server = make_server('localhost', 8000, app)
server.serve_forever()
# Gunicorn с workers
# При 10 workers'ах: 10 запросов в секунду параллельно
guniciorn --workers 10 app:application
4. Отсутствие переиспользования соединений
wsgiref не поддерживает connection pooling:
from wsgiref.simple_server import make_server
def app(environ, start_response):
# Каждый раз создаётся новое соединение к БД
import psycopg2
conn = psycopg2.connect("dbname=test") # Дорого!
cursor = conn.cursor()
# ...
conn.close()
Производственные сервера используют pooling:
from psycopg2 import pool
db_pool = pool.SimpleConnectionPool(
1, 20, # Min и max connections
"dbname=test"
)
def app(environ, start_response):
# Переиспользует существующие соединения
conn = db_pool.getconn()
# ...
db_pool.putconn(conn)
5. Отсутствие обработки ошибок и мониторинга
wsgiref не предоставляет:
- Логирование ошибок
- Мониторинг метрик
- Graceful shutdown
- Health checks
- Process management
# wsgiref просто упадёт при необработанном исключении
from wsgiref.simple_server import make_server
def app(environ, start_response):
raise ValueError("Критическая ошибка!") # Сервер рухнул?
server = make_server('localhost', 8000, app)
server.serve_forever() # Просто упадёт
6. Отсутствие безопасности
wsgiref имеет потенциальные уязвимости:
- Нет HTTPS/SSL поддержки
- Нет rate limiting
- Нет защиты от DDoS
- Использует небезопасные заголовки по умолчанию
# wsgiref не может работать с HTTPS
from wsgiref.simple_server import make_server
server = make_server('localhost', 443, app) # Не поддерживается
# Gunicorn:
gunicorn --certfile=cert.pem --keyfile=key.pem app:application
7. Отсутствие hot reload
wsgiref требует ручной перезагрузки при изменении кода.
# Production сервера поддерживают graceful reload
gunicorn --workers 4 --reload app:application
Проблемы в development тоже
Даже для разработки wsgiref не идеален:
from wsgiref.simple_server import make_server
# Проблемы:
# 1. Нет автоперезагрузки при изменении файлов
# 2. Сложный debug при ошибках
# 3. Медленно при работе с несколькими потоками (он их не поддерживает)
# 4. Нет развитого вывода информации о запросах
server = make_server('localhost', 8000, app)
server.serve_forever()
Рекомендуемые альтернативы
1. Для разработки: Django/Flask встроенный сервер
# Django
python manage.py runserver
# Автоперезагрузка, полноценный debug, лучше обработка ошибок
# Flask
from flask import Flask
app = Flask(__name__)
if __name__ == '__main__':
app.run(debug=True, reload=True)
# Автоперезагрузка, интерактивный debugger
2. Для production: Gunicorn
# Установка
pip install gunicorn
# Запуск с 4 workers'ами
gunicorn --workers 4 --threads 2 app:application
# С timeout'ом и graceful shutdown
gunicorn --workers 4 --timeout 30 --graceful-timeout 30 app:application
3. Для production: uWSGI
pip install uwsgi
# Запуск
uwsgi --http :8000 --wsgi-file app.py --callable app
4. Для асинхронных приложений: Uvicorn
pip install uvicorn
# Запуск (для ASGI приложений, FastAPI и т.п.)
uvicorn app:app --workers 4
5. Для разработки FastAPI/Starlette
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
if __name__ == "__main__":
# Встроенный сервер с автоперезагрузкой
uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)
Сравнительная таблица
| Сервер | Потоки | Production | Разработка | Скорость | Монитор |
|---|---|---|---|---|---|
| wsgiref | Нет | ❌ | ❌ | Медленно | ❌ |
| Django runserver | Да | ❌ | ✅ | Норм | ✅ |
| Flask dev | Нет | ❌ | ✅ | Норм | ✅ |
| Gunicorn | Да | ✅ | ❌ | Отлично | ✅ |
| uWSGI | Да | ✅ | ❌ | Отлично | ✅ |
| Uvicorn | Да | ✅ | ✅ | Отлично | ✅ |
Когда wsgiref вообще используется?
Редкие cases:
- Образовательные цели —学 как устроен WSGI
- Микросервисы в контролируемых условиях — на закрытой сети, 1 запрос в минуту
- Хобби проекты для друзей — когда нет требований к надёжности
- Unit тесты — имитация WSGI сервера в тестах
Практический пример: почему wsgiref плохо
from wsgiref.simple_server import make_server
import requests
import time
def app(environ, start_response):
# Запрос к внешнему API
start = time.time()
response = requests.get('https://httpbin.org/delay/2', timeout=10)
elapsed = time.time() - start
start_response('200 OK', [('Content-Type', 'text/plain')])
return [f'Waited {elapsed:.2f}s'.encode()]
server = make_server('localhost', 8000, app)
print("Сервер запущен на http://localhost:8000")
# Если открыть 5 браузеров одновременно:
# wsgiref: 5 * 2 сек = 10 сек (последовательно)
# Gunicorn (4 workers): 2 сек (параллельно на разных workers'ах)
server.serve_forever()
Выводы
❌ wsgiref НЕ рекомендуют:
- В production — отсутствие масштабируемости
- Для разработки — плохой debug, нет автоперезагрузки
- Для I/O операций — однопоточность блокирует сервер
- В критичных системах — отсутствие мониторинга
✅ Используй вместо wsgiref:
- Разработка: Django/Flask встроенный сервер или Uvicorn
- Production: Gunicorn, uWSGI или Uvicorn
- Асинхронные приложения: Uvicorn/Hypercorn
- Обучение WSGI: разберись с кодом wsgiref, но не развёртывай его