В чём отличия между Django (и Django REST Framework) и асинхронными фреймворками, такими как FastAPI, AIOHTTP и Tornado?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Отличия Django / DRF от асинхронных фреймворков
Это критически важный вопрос о современной архитектуре веб-приложений. Выбор между этими подходами влияет на производительность, масштабируемость и сложность разработки.
Django / Django REST Framework
Django и DRF — это синхронные фреймворки, основанные на потоках (thread-based). Каждый запрос обрабатывается отдельным потоком из пула.
# Django: синхронный вид
from django.views import View
from django.http import JsonResponse
from django.db import models
class UserView(View):
def get(self, request, user_id):
# Этот код выполняется в отдельном потоке
user = User.objects.get(id=user_id) # ← Синхронный запрос в БД
# Пока ждём БД, поток занят
time.sleep(2) # ← Блокирует поток
return JsonResponse({"user": user.name})
# Django REST Framework
from rest_framework.views import APIView
from rest_framework.response import Response
class UserAPIView(APIView):
def get(self, request, user_id):
user = User.objects.get(id=user_id) # ← Синхронный ORM
serializer = UserSerializer(user)
return Response(serializer.data)
Как это работает
1000 запросов
↓
Web-сервер (Gunicorn с 4 рабочими потоками)
├── Поток 1: обрабатывает запрос 1, ждёт БД (2 сек)
├── Поток 2: обрабатывает запрос 2, ждёт БД (2 сек)
├── Поток 3: обрабатывает запрос 3, ждёт БД (2 сек)
└── Поток 4: обрабатывает запрос 4, ждёт БД (2 сек)
↓
(Остальные 996 запросов ждут в очереди...)
FastAPI (Асинхронный)
FastAPI — асинхронный фреймворк на базе ASGI, использует async/await. Один процесс обрабатывает тысячи запросов.
# FastAPI: асинхронный вид
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(user_id: int, session: AsyncSession):
# Этот код может быть приостановлен
user = await session.get(User, user_id) # ← Асинхронный запрос в БД
# Пока ждём БД, поток освобождается для других запросов
await asyncio.sleep(2) # ← Не блокирует поток
return {"user": user.name}
Как это работает
1000 запросов
↓
Web-сервер (Uvicorn с 1 процессом)
├── Event Loop обрабатывает все 1000 запросов одновременно
│ ├── Запрос 1: ждёт БД → переключается на запрос 2
│ ├── Запрос 2: ждёт БД → переключается на запрос 3
│ └── ... и так далее
↓
Все запросы обрабатываются за ~2 сек (вместо 500 сек)
Основные различия
| Параметр | Django/DRF | FastAPI | AIOHTTP | Tornado |
|---|---|---|---|---|
| Модель | Синхронная (потоки) | Асинхронная (event loop) | Асинхронная | Асинхронная |
| ASGI/WSGI | WSGI | ASGI | ASGI | Свой протокол |
| Async поддержка | Ограниченная (Django 3.1+) | Полная | Полная | Полная |
| ORM | Django ORM (синхронный) | SQLAlchemy async | Свой или внешний | SQLAlchemy async |
| Скорость запуска | Быстро | Быстро | Быстро | Быстро |
| Производительность | Средняя (I/O) | Высокая | Высокая | Высокая |
| Потребление памяти | Высокое (потоки) | Низкое | Низкое | Низкое |
| Простота | Очень просто | Просто | Средне | Средне |
| Батарейки в комплекте | Много (auth, admin, migrations) | Мало (fastapi-users, alembic) | Мало | Мало |
| Сообщество | Огромное | Растёт быстро | Среднее | Среднее |
| Документация | Отличная | Отличная | Хорошая | Хорошая |
| Масштабируемость | Плохая (нужны потоки) | Отличная | Отличная | Отличная |
Сравнение производительности
# Тест: 1000 запросов к API, которая делает запрос в БД (1 сек)
# Django с 4 потоками Gunicorn:
# 1000 запросов ÷ 4 потока × 1 сек = 250 секунд
# FastAPI с 1 процессом Uvicorn:
# 1000 запросов (параллельно) × 1 сек = ~1 сек
# Разница: 250x быстрее!
Практический пример: запросы к внешнему API
Django (ПЛОХО - синхронно)
import requests
from django.views import View
from django.http import JsonResponse
class ExternalDataView(View):
def get(self, request):
# Каждый запрос блокирует поток на ~2 сек
data1 = requests.get("https://api1.com/data").json() # 2 сек
data2 = requests.get("https://api2.com/data").json() # 2 сек
data3 = requests.get("https://api3.com/data").json() # 2 сек
# Всего: 6 секунд (последовательно)
return JsonResponse({
"data1": data1,
"data2": data2,
"data3": data3,
})
# Для N параллельных запросов нужно N потоков!
# 100 запросов = 100 потоков × 8 МБ = 800 МБ памяти
FastAPI (ХОРОШО - асинхронно)
import httpx
from fastapi import FastAPI
app = FastAPI()
@app.get("/data")
async def get_data():
async with httpx.AsyncClient() as client:
# Все 3 запроса выполняются параллельно
results = await asyncio.gather(
client.get("https://api1.com/data"),
client.get("https://api2.com/data"),
client.get("https://api3.com/data"),
)
# Всего: ~2 сек (параллельно!)
return {
"data1": results[0].json(),
"data2": results[1].json(),
"data3": results[2].json(),
}
# Для N параллельных запросов нужна 1 корутина!
# 100 запросов = 1 процесс × 50 МБ
AIOHTTP
AIOHTTP — асинхронный фреймворк, более низкоуровневый, чем FastAPI.
import aiohttp
from aiohttp import web
async def handle_request(request):
async with aiohttp.ClientSession() as session:
async with session.get("https://api.example.com/data") as resp:
data = await resp.json()
return web.json_response(data)
app = web.Application()
app.router.add_get("/data", handle_request)
if __name__ == "__main__":
web.run_app(app)
Tornado
Tornado — один из первых асинхронных фреймворков в Python, хорош для WebSocket.
import tornado.ioloop
import tornado.web
import tornado.httpclient
class MainHandler(tornado.web.RequestHandler):
async def get(self):
client = tornado.httpclient.AsyncHTTPClient()
response = await client.fetch("https://api.example.com/data")
self.write({"data": response.body})
def make_app():
return tornado.web.Application([
(r"/data", MainHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
Когда использовать Django/DRF?
Используй Django/DRF, если:
- Традиционное веб-приложение (с шаблонами)
- Мало параллельных I/O операций
- Нужны встроенные функции (auth, admin, migrations)
- Нужно быстро разработать MVP
- Команда опытна в Django
# Идеально подходит
from django.contrib.auth.decorators import login_required
from django.db import models
class BlogPost(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
@login_required
def view_post(request, post_id):
post = BlogPost.objects.get(id=post_id) # Встроенная авторизация
return render(request, "post.html", {"post": post})
Когда использовать FastAPI?
Используй FastAPI, если:
- Разрабатываешь API (REST или GraphQL)
- Нужна высокая производительность
- Много параллельных запросов (WebSocket, Server-Sent Events)
- Новый проект (можешь учиться на лучших практиках)
- SPA фронтенд (React, Vue, Next.js)
# Идеально подходит
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Echo: {data}")
Можно ли использовать async в Django?
Да, но сложно! Django 3.1+ поддерживает async views, но:
- ORM всё ещё синхронный (Django 4.1 добавил частичную поддержку)
- Нужно использовать
async_to_sync()/sync_to_async() - Меньше документации и примеров
# Django async view (не рекомендуется)
from django.views import View
from asgiref.sync import sync_to_async
class AsyncUserView(View):
async def get(self, request):
# ORM синхронный, нужно обёртка
user = await sync_to_async(User.objects.get)(id=1)
return JsonResponse({"user": user.name})
Миграция с Django на FastAPI
# Типичный путь:
# 1. FastAPI для API
# 2. Django только для админки и статики
# 3. Полный переход на FastAPI
# Структура проекта
project/
├── api/ # FastAPI (новое)
│ ├── main.py
│ └── routes/
└── admin/ # Django (старое)
├── manage.py
└── views.py
Производительность на примере обработки изображений
# Django (синхронно)
def process_images(request):
for i in range(100):
image = Image.open(f"image_{i}.jpg")
image.filter(ImageFilter.BLUR) # 0.1 сек за image
# Всего: 10 секунд (поток занят)
return JsonResponse({"status": "done"})
# FastAPI (асинхронно)
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=4)
@app.post("/process-images")
async def process_images():
def process_image(i):
image = Image.open(f"image_{i}.jpg")
return image.filter(ImageFilter.BLUR)
# Обрабатываем 4 изображения одновременно
results = await asyncio.gather(*[
asyncio.get_event_loop().run_in_executor(
executor, process_image, i
)
for i in range(100)
])
# Всего: ~2.5 сек (параллельно 4 потоками, но в главном event loop)
return {"status": "done"}
Итоговый вывод
| Выбирай | Если |
|---|---|
| Django/DRF | Традиционное веб-приложение, MPA, встроенные батарейки |
| FastAPI | API, SPA, высокая производительность, микросервисы |
| AIOHTTP | Low-level async контроль, специальные требования |
| Tornado | Real-time приложения, WebSocket, очень нагруженные |
Современный тренд:
- Новые проекты → FastAPI
- Существующие → Django (может быть постепенная миграция)
- Высоконагруженные → FastAPI или AIOHTTP
Ключевой момент: Асинхронные фреймворки дают 100-1000x выигрыш в производительности для I/O-bound приложений!