open atlas
↑ К треку
Архитектура бэкенда BE · 05 · 03

Стратегии retry: backoff, jitter и thundering herd

Exponential backoff без jitter создаёт синхронизированные retry storm. Full jitter размазывает retry по времени. Retry-After и retry budget не дают восстанавливающемуся сервису снова упасть.

BE Middle ◷ 14 min
Уровень
ОсновыJuniorMiddleSenior

Сервис возвращает 503. Тысяча клиентов планирует retry ровно через 2 секунды. В T+2с тысяча запросов бьёт восстанавливающийся сервер одновременно — тот самый спайк, который сломал его в первый раз. Механизм retry только что усилил outage.

Exponential backoff: базовая формула

Плохо настроенный retry не просто не помогает — он может превратить 30-секундный сбой в 30-минутный outage. Вот почему, и что делать вместо этого.

Минимально жизнеспособный retry loop (петля повторных попыток) ждёт дольше перед каждой следующей попыткой:

delay = min(cap, base × 2^attempt)

base обычно 100–500 мс. cap обычно 30–60 с. Попытки ограничены 3–6 в зависимости от критичности операции.

С base = 200мс и cap = 30с:

  • Попытка 1 → ждём 200 мс
  • Попытка 2 → ждём 400 мс
  • Попытка 3 → ждём 800 мс
  • Попытка 4 → ждём 1 600 мс
  • Попытка 8 → ждём 30 с (cap)

Проблема: если 1000 клиентов упали одновременно, все выберут одну задержку и ретраят в один и тот же момент — синхронизированный спайк и есть thundering herd.

Jitter: размазываем retry по времени

Три варианта jitter (названы в AWS Architecture Blog):

Full jitter (рекомендован по умолчанию):

delay = random(0, min(cap, base × 2^attempt))

Выбирает случайное значение от 0 до экспоненциального cap. Самое плавное распределение. Проще всего реализовать.

Equal jitter:

delay = (base × 2^attempt)/2 + random(0, (base × 2^attempt)/2)

Половина детерминирована, половина случайна. Сложнее рассуждать.

Decorrelated jitter (используется в AWS SDK v3):

delay = min(cap, random(base, prev_delay × 3))

Окно каждого retry зависит от предыдущей задержки. Растёт органично. Эмпирически похож на full jitter по профилю нагрузки.

Избегай чистого exponential backoff без jitter — это учебниковая thundering-herd ловушка.

Тип jitterФормулаИспользуется в
Fullrandom(0, min(cap, base × 2^n))Большинство SDK
Equalhalf_fixed + random(0, half_fixed)Некоторые Java-библиотеки
Decorrelatedrandom(base, prev × 3) cappedAWS SDK v3
Нетmin(cap, base × 2^n)Thundering herd

Что ретраить — и что нет

Прежде чем добавить retry к любому клиентскому вызову, спроси себя: если запрос вернёт 400, поможет ли повтор? Ответ определяет, нужен ли retry вообще.

Условия retry важны не меньше, чем тайминг:

Ретрай: 5xx-ошибки, network timeout, connection refused, 429 Too Many Requests (с Retry-After).

Не ретрай: 4xx-ошибки (кроме 408 и 429) — запрос клиента некорректен, retry даст тот же 4xx. 410 Gone — ресурс окончательно отсутствует.

Многие библиотеки наивно повторяют попытку для всего не-2xx, порождая retry storm на клиентские баги. Явно перечисляй условие retry в конфигурации.

Retry-After: подсказка сервера по backoff

Когда сервер возвращает 429 или 503, он может включить Retry-After: 30 (секунды) или абсолютную дату. Хорошо настроенный клиент уважает это перед применением своего backoff. gRPC использует трейлер grpc-retry-pushback-ms для того же.

Клиент, игнорирующий Retry-After и применяющий короткий собственный backoff — это thundering-herd в действии. Сервер попросил передышку, клиент отказал.

Retry budget: ограничиваем суммарный retry по call site

Индивидуальных ограничений попыток (3–6) недостаточно на масштабе. Retry budget — token-bucket допустимых retry в секунду по всему call site. Когда bucket опустел, клиент открывает circuit и возвращает детерминированную ошибку — перестаёт бить downstream.

Без retry budget outage сервиса заставляет каждого клиента сжечь свои 6 попыток одновременно, умножая трафик в 6 раз и превращая 30-секундный блип в минутный outage.

Почему это работает

Netflix и Google опубликовали tail-latency статьи, показывающие каскад от uncapped retry: медленный downstream вызывает retry, retry увеличивает нагрузку на downstream, повышенная нагрузка создаёт ещё больше медленных ответов, что создаёт ещё больше retry. Retry budget ломает этот цикл, делая rate retry жёстким ограничением, а не per-request политикой.

Викторина

Зачем добавлять jitter поверх exponential backoff?

Викторина

Сервер возвращает 503 с Retry-After: 30. Три клиента: A уважает Retry-After, B игнорирует и использует 1с backoff, C не ретраит вообще. Какой клиент ведёт себя корректно?

Расставь шаги по порядку

Поставь шаги thundering herd сценария в правильном порядке (без jitter, 1000 клиентов):

  1. 1 Все 1000 клиентов POST-ят на сервер; downstream перегружен и возвращает 503
  2. 2 Все 1000 клиентов планируют retry ровно на T + 1с (без jitter)
  3. 3 В T + 1с все 1000 ретраят одновременно — тот же спайк, что сломал сервер
  4. 4 Сервер снова возвращает 503; все 1000 планируют T + 2с
  5. 5 Паттерн повторяется бесконечно, не давая сервису восстановиться
Вспомните перед уходом
  1. 01
    Объясни, почему retry loop без jitter опаснее исходного сбоя, который он пытается восстановить.
  2. 02
    Что такое full jitter формула и что означает каждая переменная?
  3. 03
    Что такое retry budget и почему индивидуального ограничения в 6 попыток недостаточно?
Итог

Exponential backoff — min(cap, base × 2^n) — увеличивает ожидание между попытками, но без jitter синхронизирует всех клиентов на один момент и вызывает thundering herd (синхронный штурм). Full jitter добавляет random(0, ...) для равномерного размазывания. AWS SDK v3 использует decorrelated jitter с аналогичным профилем нагрузки. Ретраить только 5xx и timeout, не 4xx. Уважать заголовок Retry-After из ответов 429/503. Ограничить попытки 3–6 на запрос и применять retry budget (token-bucket) по call site, чтобы не дать failing downstream умереть от каждого retry-цикла. Теперь, когда сервис восстанавливается минуты там, где достаточно 30 секунд — смотри на клиентов: синхронизированные retry без jitter почти всегда и есть усилитель проблемы.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 5 завершено
Связанные уроки
встречается в188

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.