Таймауты, retry с backoff и jitter

↑ 7.3 Надёжность и отказоустойчивость · → Следующая

Желательно~3 мин чтенияУровень: senior

Зачем это на собесе

Основа устойчивых вызовов: без таймаута и разумных ретраев сбой одного сервиса каскадно роняет остальные.

Объяснение

Таймаут — верхняя граница ожидания. Без неё вызов может висеть вечно и занимать ресурсы (поток, соединение, память).

Уровни таймаутов: соединение (connect), одна попытка, общий бюджет операции (deadline). Внутренние таймауты должны быть меньше внешних: сумма попыток укладывается в общий срок.

Retry повторяет неуспешную попытку. Правила:

ПравилоПояснение
Только временные ошибки5xx, таймаут, сетевые сбои, 429; не 400/404
Только идемпотентные операцииили с ключом идемпотентности
Ограниченное число попытокобычно 3–5
Экспоненциальная задержка100 мс, 200, 400, 800…
Jitter (случайный разброс)чтобы клиенты не повторяли синхронно (thundering herd)
Учитывать Retry-Afterсервер сам задаёт паузу при 429/503
Бюджет ретраевограничение доли повторов, чтобы не утроить нагрузку на проблемный сервис
var delay = TimeSpan.FromMilliseconds(Math.Min(cap, baseMs * Math.Pow(2, attempt)));
delay += TimeSpan.FromMilliseconds(Random.Shared.NextDouble() * delay.TotalMilliseconds);   // full jitter

Усиление ретраев: 3 слоя по 3 попытки = 27 запросов на один исходный. Ретраи делайте на одном слое (обычно ближе к вызывающему).

Нюансы и подводные камни

  • Ретрай неидемпотентного POST без ключа — двойные списания.
  • Таймаут клиента не отменяет работу на сервере: сервер тоже должен реагировать на отмену.
  • Бесконечные ретраи превращают сбой в DoS собственной системы.
  • Ретраи при перегрузке усугубляют её: сочетайте с circuit breaker и rate limiting.
  • Логируйте попытки и итог, метрики числа ретраев.

Практика

  1. Настройте таймауты и retry с jitter для клиента внешнего API.
  2. Сымитируйте сбой и проследите число запросов на серверной стороне.
  3. Рассчитайте общий бюджет времени при 3 попытках по 2 секунды.

Вопросы с ответами

Связанные темы