Как FastAPI обрабатывает синхронные функции в эндпоинтах

Ответ

FastAPI, будучи асинхронным фреймворком, имеет встроенный механизм для корректной обработки синхронных функций-обработчиков (def вместо async def).

Принцип работы:

Если FastAPI обнаруживает, что функция-обработчик эндпоинта является стандартной синхронной функцией, он не выполняет ее напрямую в основном цикле событий (event loop). Вместо этого он запускает эту функцию в отдельном пуле потоков (ThreadPoolExecutor).

Почему это важно?

Это предотвращает блокировку event loop'а. Если бы долгая синхронная операция (например, работа с файлами, обращение к старой библиотеке без async поддержки) выполнялась в основном потоке, сервер не смог бы обрабатывать другие запросы, пока она не завершится. Вынос в отдельный поток позволяет event loop'у продолжать принимать и обрабатывать другие запросы.

Пример синхронного эндпоинта:

import time
from fastapi import FastAPI

app = FastAPI()

# Эта функция будет выполнена в отдельном потоке
def process_data_sync():
    # Имитация долгой, блокирующей операции
    time.sleep(2)
    return {"status": "done"}

@app.get("/process")
def run_processing():
    # FastAPI автоматически вызовет process_data_sync в пуле потоков
    result = process_data_sync()
    return result

Рекомендации:

  • Для I/O-операций (работа с сетью, базами данных) всегда предпочитайте асинхронные функции (async def) с await, так как это более эффективно и масштабируемо.
  • Для тяжелых CPU-bound задач (сложные вычисления) этот механизм не является оптимальным. Для таких случаев лучше использовать фоновые задачи (BackgroundTasks) или выносить их в отдельные воркеры (например, Celery), чтобы не нагружать потоки, обслуживающие веб-запросы.