Вопрос 19 · Раздел 17

Что такое паттерн Retry и как его правильно использовать

Structured Java interview answer with junior, middle, and senior-level explanation.

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

Retry — автоматический повторный вызов сервиса при временной ошибке.

Вызов 1 → ошибка (timeout)
Retry 1 → ошибка (timeout)
Retry 2 → успех! ✅

Зачем: Временные ошибки бывают часто (сеть, таймаут, перегрузка). Retry даёт сервису время восстановиться.


🟡 Middle Level

Resilience4j Retry

RetryConfig config = RetryConfig.custom()
    .maxAttempts(3)  // 3 попытки
    .waitDuration(Duration.ofSeconds(1))  // ждать 1 сек между попытками
    .retryExceptions(TimeoutException.class, ConnectException.class)
    .build();

Retry retry = Retry.of("backend", config);

Supplier<String> decorated = Retry.decorateSupplier(retry, 
    () -> backendService.call());

Exponential backoff

RetryConfig config = RetryConfig.custom()
    .maxAttempts(5)
    .waitDuration(Duration.ofMillis(500))
    .intervalFunction(IntervalFunction.ofExponentialBackoff(500, 2.0))
    .build();

// 500 = начальная задержка (мс), 2.0 = множитель (exponential).
// 500ms → 1000ms → 2000ms → 4000ms ...

// Попытки: 500ms → 1s → 2s → 4s → 8s

Типичные ошибки

  1. Retry для всех ошибок:
    404 Not Found → retry бесполезен
    500 Internal Error → retry может помочь
    Решение: retry только для retryable ошибок
    

🔴 Senior Level

Retry с jitter

// Jitter предотвращает thundering herd
// Thundering herd — когда множество клиентов одновременно повторяют вызов,
// создавая новый пик нагрузки. Jitter «размазывает» повторные вызовы во времени.
Random random = new Random();
long waitTime = baseDelay + random.nextInt((int)(baseDelay * 0.1)); // jitter 10% от delay

Production Experience

Spring Retry:

@Retryable(
    value = {RetryableException.class},
    maxAttempts = 3,
    backoff = @Backoff(delay = 1000, multiplier = 2)
)
// @Recover — метод, вызываемый когда все retry исчерпаны.
// Сигнатура: тот же тип возвращаемого значения + Exception как первый параметр.
public String callService(String input) {
    return restTemplate.getForObject(url, String.class);
}

@Recover
public String recover(RetryableException e) {
    return "fallback";
}

Best Practices

✅ Exponential backoff
✅ Jitter для предотвращения thundering herd
✅ Retry только для retryable ошибок
✅ Лимит попыток
✅ Fallback после всех retry

❌ Retry для 4xx ошибок
❌ Без backoff
❌ Без лимита попыток
❌ Retry для идемпотентных операций

🎯 Шпаргалка для интервью

Обязательно знать:

  • Retry — автоматический повторный вызов при временной ошибке (timeout, network)
  • Exponential backoff: 500ms → 1s → 2s → 4s — даёт сервису время восстановиться
  • Jitter добавляет случайность для предотвращения thundering herd
  • Retry только для retryable ошибок (TimeoutException, ConnectException)
  • 4xx (Not Found, Bad Request) — retry бесполезен, сервер не починится
  • maxAttempts обязателен — бесконечный retry = hang
  • Fallback после исчерпания всех попыток

Частые уточняющие вопросы:

  • Что такое thundering herd? Множество клиентов одновременно повторяют вызов — jitter «размазывает» повторы.
  • Какие ошибки retryable? Timeout, connection refused, 5xx. НЕ retryable: 4xx, business exceptions.
  • Exponential backoff формула? delay = baseDelay * multiplier^attempt, например 500ms, 1s, 2s, 4s.
  • Spring Retry vs Resilience4j? Spring Retry проще, Resilience4j гибче (bulkhead, circuit breaker, time limiter).

Красные флаги (НЕ говорить):

  • “Retry для всех ошибок” — нет, 404 retry бесполезен
  • “Retry без лимита — надёжность” — нет, бесконечный retry = hang системы
  • “Jitter не нужен, серверы справятся” — без jitter thundering herd
  • “Retry = идемпотентность не важна” — критична, duplicate request возможен

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

  • [[20. Что такое exponential backoff]]
  • [[17. Как обеспечить отказоустойчивость микросервисов]]
  • [[5. Что такое паттерн Circuit Breaker]]
  • [[4. Что такое компенсирующие транзакции]]
  • [[15. Как организовать коммуникацию между микросервисами]]