Как поступить при медленном ответе стороннего сервиса, влияющем на производительность сайта
Что проверяют
Понимаете ли вы, что медленная зависимость способна положить весь сайт: воркеры заняты ожиданием, новые запросы копятся в очереди. Следом спрашивают, что увидит пользователь, если сервис не отвечает совсем, и где вы поставите таймаут.
Как отвечать
Сначала измерьте: какой эндпоинт и какой перцентиль (p95, p99) деградирует, сколько вызовов сервиса приходится на одну страницу. Метрики через prometheus_client или OpenTelemetry, графики в Grafana.
Дальше меры:
- таймаут всегда: у
requestsтаймаута по умолчанию нет, запрос может висеть бесконечно, поэтомуtimeout=(3.05, 5)обязателен. Вhttpxпо умолчанию 5 секунд, вaiohttpлимиты задаются черезClientTimeout; - вынести из запроса пользователя: если результат не нужен прямо в ответе, отдать работу в Celery или RQ, а страницу рендерить с плейсхолдером;
- кэш: Redis с TTL под нужную свежесть, лучше с отдачей устаревшего значения, пока фоновая задача его обновляет. Одновременный промах закрывается блокировкой, иначе сотня запросов разом пойдёт в медленный сервис;
- повторы с умом:
tenacityс экспоненциальной задержкой и джиттером, только для идемпотентных вызовов и с общим лимитом времени; - circuit breaker: после серии ошибок на время перестать ходить в сервис и сразу отдавать запасной ответ;
- изоляция: лимит параллельных вызовов, например
asyncio.Semaphore, чтобы одна зависимость не съела все воркеры.
Асинхронность помогает, только если стек асинхронный целиком. aiohttp внутри синхронного представления Django ничего не даст, а requests внутри асинхронного эндпоинта FastAPI заблокирует цикл событий.
Где спотыкаются
Отвечают «переписать на asyncio». Сервис от этого быстрее не станет, а при gunicorn с синхронными воркерами выигрыш нулевой.
Ставят повторы без таймаута и лимита. Медленный сервис получает втрое больше запросов и окончательно ложится.
Глотают исключения через except Exception и возвращают None. Ошибка исчезает из логов, а пользователь видит пустой блок без объяснений.