Как решить проблему синхронного сервера с длительной обработкой запросов при множестве клиентов
Что проверяют
Различаете ли вы I/O-bound и CPU-bound нагрузку и понимаете ли, какой инструмент к какой подходит. Следом обычно спрашивают про GIL и про то, что делать, если обработка занимает минуты.
Как отвечать
Сначала диагноз: чем занят запрос. Ожидание внешнего API или базы и тяжёлые вычисления лечатся по-разному.
- больше воркеров: у gunicorn по умолчанию класс
syncи один воркер, то есть один запрос одновременно. Первым делом проверяют--workers(часто начинают с «2 × ядра + 1») и--threadsдля классаgthread. Для I/O потоки работают нормально: GIL отпускается на время ожидания сети и диска; - асинхронность: ASGI-стек (FastAPI или Starlette на uvicorn, aiohttp) держит тысячи ожидающих соединений в одном процессе. Условие одно: весь путь запроса неблокирующий, драйверы вроде
asyncpgиhttpx.AsyncClient, а редкий синхронный вызов уходит вasyncio.to_thread(); - процессы для вычислений: CPU-bound задача в потоке упирается в GIL, её выносят в
ProcessPoolExecutorс ограниченным пулом. Сборки Python без GIL уже существуют, но это отдельная сборка интерпретатора, а не поведение по умолчанию; - очередь для долгих задач: если обработка длится дольше нескольких секунд, запрос кладёт задачу в Celery, RQ или Dramatiq и сразу отвечает
202 Acceptedс id. Результат клиент узнаёт опросом статуса, по вебхуку или через WebSocket.
Добавьте про защиту: таймауты на внешние вызовы и ограничение параллелизма через asyncio.Semaphore или размер пула, чтобы пик нагрузки не превращался в лавину.
Где спотыкаются
Пишут async def и внутри зовут requests.get или time.sleep. Такой вызов блокирует цикл событий, и асинхронный сервер снова обслуживает по одному запросу.
Запускают multiprocessing.Process на каждый запрос. Процессы плодятся без лимита, и сервер ложится от нагрузки, которую должен был выдержать.
Отдают долгую задачу в ThreadPoolExecutor внутри веб-воркера по принципу «запустил и забыл». При перезапуске воркера задача молча теряется, повторов и статуса нет.