Как правильно настроить повторные попытки и fallback при работе с внешними API

В прошлой статье разобрали единое логирование и контроль лимитов запросов. Сегодня продолжим тему — поговорим про повторные попытки и стратегии отката. Это та часть, которую многие команды упускают до первых серьёзных сбоев в продакшене.

Какие ошибки можно повторять, а какие — категорически нет

Главная ошибка при настройке retry — пытаться повторить любой сбой. Это приводит к замораживанию системы, лишним тратам и даже ломке данных на стороне провайдера.

Можно безопасно повторять:

  • Таймауты соединения и сетевые ошибки
  • Ошибки 5xx на стороне внешнего сервиса
  • Временная перегрузка провайдера (429 Too Many Requests)
  • Кратковременные сбои сети

Повторять ни в коем случае нельзя:

  • Ошибки авторизации 401 и 403
  • Ошибки валидации параметров 400
  • Ошибки не найдено 404
  • Любые бизнес-ошибки с явным указанием на некорректность запроса

Почему важна экспоненциальная задержка

Если повторять запросы сразу с одинаковым интервалом, при сбое провайдера вы начнёте бомбить его ещё сильнее. Проблема только усугубится, сервис упадёт надолго.

Правильный подход — экспоненциальная задержка: каждый следующий повтор делается через всё больший промежуток времени. Дополнительно рекомендуется добавить небольшой случайный сдвиг (jitter), чтобы избежать пиков одновременно от всех клиентов.

Базовый пример на Python:

import time import random def exponential_backoff(attempt: int, base_delay: float = 1.0, max_delay: float = 32.0) -> float: delay = min(base_delay * (2 ** attempt), max_delay) jitter = delay * random.uniform(0, 0.1) return delay + jitter # Пример использования for attempt in range(5): try: # запрос к внешнему API response = make_request() break except RetryableError: if attempt == 4: raise time.sleep(exponential_backoff(attempt))

Три стандартные стратегии fallback

Retry не решает всех проблем. Если внешний сервис лежит долго, система должна уметь работать дальше. Есть три распространённых подхода:

  • **Очередь запросов:** запросы складываются в буфер и отправляются позже, когда сервис восстановится. Подходит для необязательных операций.
  • **Данные из кэша:** возвращаем последний успешный ответ. Подходит для справочной информации, которая не критично устаревает.
  • **Альтернативный провайдер:** автоматически переключаемся на резервный сервис. Самый надёжный вариант, но дороже в реализации.

Практические рекомендации

Всегда ограничивайте общее количество повторений. Никогда не делайте бесконечный retry — это верный способ положить собственную систему вслед за внешним сервисом.

  • Для каждого внешнего сервиса заведите отдельные настройки retry и fallback
  • Обязательно логируйте все повторные попытки и причины отката
  • Мониторьте процент ошибок и количество повторений — это индикатор проблем с провайдером
  • Не стесняйтесь отказывать в обработке, если внешний сервис долго недоступен и нет адекватного fallback

Если вам нужен готовый production-класс обёртки с retry, jitter и fallback на Python — я добавил полную реализацию и шаблоны в материалы телеграм-канала, ссылка в профиле.

В комментариях поделитесь, какие стратегии отката используете вы в своих проектах.