Ответ
Чтобы не блокировать пользователя во время выполнения длительных операций на сервере (например, генерации отчета или сложного рендеринга), применяют несколько подходов, которые выносят работу из основного потока обработки запроса.
Основной принцип — как можно быстрее вернуть пользователю первоначальный ответ, а тяжелую задачу выполнить в фоне.
1. Фоновые задачи (Background Tasks)
Идеально для тяжелых, ресурсоемких операций, которые могут выполняться несколько секунд или минут. Клиент получает ответ немедленно, а результат задачи можно получить позже через другой эндпоинт или WebSocket.
- Инструменты: Celery, RQ (Redis Queue).
# tasks.py
from celery import Celery
app = Celery('tasks', broker='redis://localhost:6379/0')
@app.task
def generate_report_async(user_id: int):
# ... логика генерации тяжелого отчета ...
print(f"Report for user {user_id} is ready!")
return True
# main.py (например, FastAPI)
@app.post("/reports/")
def create_report(user_id: int):
generate_report_async.delay(user_id)
return {"message": "Report generation started. You will be notified upon completion."}
2. Асинхронные фреймворки
Подходят для I/O-bound операций (запросы к базам данных, внешним API), где основной поток ждет ответа от внешнего ресурса. async/await позволяет эффективно переключать контекст, не блокируя весь процесс.
- Инструменты: FastAPI, aiohttp, Quart.
# FastAPI
import asyncio
from fastapi import FastAPI
app = FastAPI()
async def fetch_data_from_external_api():
# Имитация долгого сетевого запроса
await asyncio.sleep(5)
return {"data": "some important data"}
@app.get("/data")
async def get_data():
# Этот вызов не блокирует сервер, он может обрабатывать другие запросы
external_data = await fetch_data_from_external_api()
return {"content": external_data}
3. Потоковая передача данных (Streaming)
Используется, когда контент можно генерировать и отдавать по частям. Клиент начинает получать данные сразу, не дожидаясь полной готовности.
- Инструменты: WebSockets, Server-Sent Events (SSE), StreamingResponse в FastAPI/Starlette.
# FastAPI StreamingResponse
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio
app = FastAPI()
async def slow_data_generator():
for i in range(10):
yield f"Data chunk {i}n"
await asyncio.sleep(1)
@app.get("/stream")
async def stream_data():
return StreamingResponse(slow_data_generator())