Питання 5 · Розділ 17

Що таке патерн Circuit Breaker

Cascade failure (ланцюговий збій): сервіс A чекає сервіс B, сервіс C чекає A — усі потоки зависають, і вся система падає.

Мовні версії: English Russian Ukrainian

🟢 Junior Level

Circuit Breaker (запобіжник) — патерн, який захищає сервіс від виклику сервісу, що не працює.

Аналогія з життя: електричний запобіжник вимикає струм при перевантаженні, щоб не згоріла проводка.

Три стани:

  1. CLOSED — звичайний режим, виклики проходять
  2. OPEN — виклики блокуються (сервіс “зламаний”)
  3. HALF-OPEN — пробний виклик (перевірка, чи полагоджений сервіс)
Сервіс A → Сервіс B (працює)
              ↓ (почав гальмувати)
Circuit Breaker → OPEN (блокує виклики)
              ↓ (почекали, перевірка)
Circuit Breaker → HALF-OPEN → один виклик для перевірки
              ↓ (успіх!)
Circuit Breaker → CLOSED (знову працює)

🟡 Middle Level

Як працює

// Resilience4j
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
    .failureRateThreshold(50)  // 50% — компроміс: не відкриваємо від однієї випадкової помилки.
    // slidingWindowSize=10, minimumNumberOfCalls=5: потрібно мінімум 5 викликів,
    // і якщо 50%+ з них FAILED — breaker відкривається.
    .waitDurationInOpenState(Duration.ofSeconds(10))  // чекати 10 сек
    .slidingWindowSize(10)  // останні 10 викликів
    .build();

CircuitBreaker circuitBreaker = CircuitBreaker.of("backend", config);

// Використання
Supplier<String> decorated = CircuitBreaker
    .decorateSupplier(circuitBreaker, () -> backendService.call());

Try.of(decorated)
    .onSuccess(result -> process(result))
    .onFailure(error -> fallback());

Коли використовувати

✅ Circuit Breaker потрібен коли:

  • Зовнішній сервіс може бути недоступний
  • Мережеві проблеми (timeout, connection refused)
  • Потрібно захистити свій сервіс від cascade failure

Cascade failure (ланцюговий збій): сервіс A чекає сервіс B, сервіс C чекає A — усі потоки зависають, і вся система падає.

❌ Circuit Breaker не потрібен коли:

  • Виклик завжди швидкий і надійний
  • Внутрішні виклики в одному процесі

Типові помилки

  1. Занадто агресивний threshold:
    failureRateThreshold = 10% → OPEN після 1 помилки з 10
    Занадто багато false positives
    

🔴 Senior Level

Internal Implementation

State transitions:

CLOSED → OPEN: failure rate > threshold
OPEN → HALF-OPEN: wait duration elapsed
HALF-OPEN → CLOSED: success call
HALF-OPEN → OPEN: failure call

Sliding window:

COUNT-based: останні N викликів
TIME-based: виклики за останні T секунд

Resilience4j підтримує обидва типи

Архітектурні Trade-offs

Підхід Плюси Мінуси
Circuit Breaker Швидкий fallback, бо не тратить час на очікування timeout від сервісу, що не працює — одразу повертає заглушку. Складне налаштування
Retry Пробує знову Може погіршити проблему
Timeout Захист від зависання Не захищає від помилок

Production Experience

Spring Boot + Resilience4j:

@CircuitBreaker(name = "backend", fallbackMethod = "fallback")
@Retry(name = "backend")
@TimeLimiter(name = "backend")
public CompletableFuture<String> callBackend(String input) {
    return backendService.callAsync(input);
}

public CompletableFuture<String> fallback(String input, RuntimeException ex) {
    return CompletableFuture.completedFuture("fallback: " + input);
}

Configuration:

resilience4j:
  circuitbreaker:
    instances:
      backend:
        slidingWindowSize: 10
        failureRateThreshold: 50
        waitDurationInOpenState: 10s
        permittedNumberOfCallsInHalfOpenState: 3
        minimumNumberOfCalls: 5

Best Practices

✅ Налаштовуйте threshold за метриками
✅ Використовуйте fallback для graceful degradation
✅ Моніторьте стан Circuit Breaker
✅ Комбінуйте з Retry і Timeout

❌ Не використовуйте занадто низький threshold
❌ Не ігноруйте HALF-OPEN стан
❌ Не використовуйте без fallback

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • Circuit Breaker захищає від cascade failure, блокуючи виклики до сервісу, що не працює
  • Три стани: CLOSED (норма), OPEN (блокування), HALF-OPEN (перевірка)
  • Перехід CLOSED→OPEN при перевищенні failure rate threshold (зазвичай 50%)
  • OPEN→HALF-OPEN після wait duration (10-60 сек), HALF-OPEN→CLOSED при успіху
  • Sliding window (10-100 викликів) для статистично значущої вибірки
  • Завжди комбінуйте з fallback для graceful degradation
  • Resilience4j — стандартна бібліотека для Java/Spring

Часті уточнюючі питання:

  • Чому 50% threshold? Компроміс: не відкриваємо від однієї випадкової помилки, але і не чекаємо поки все впаде.
  • Навіщо HALF-OPEN? Перевірити, чи полагоджений сервіс, перш ніж відновити повний трафік.
  • Чим Circuit Breaker кращий простого timeout? Timeout захищає від зависання, але не від cascade failure — CB одразу повертає fallback, не витрачаючи час.
  • Як рахувати failure rate? Sliding window: останні N викликів або виклики за T секунд.

Червоні прапорці (НЕ говорити):

  • “Circuit Breaker = Retry” — ні, CB блокує, Retry пробує знову
  • “Threshold 10% — надійність” — ні, буде багато false positives
  • “CB не потрібен для внутрішніх сервісів” — cascade failure можливий всюди
  • “HALF-OPEN можна пропустити” — без нього не дізнатись, чи полагоджений сервіс

Пов’язані теми:

  • [[6. Як працює Circuit Breaker і які у нього стани]]
  • [[17. Як забезпечити відмовостійкість мікросервісів]]
  • [[18. Що таке патерн Bulkhead]]
  • [[19. Що таке патерн Retry і як його правильно використовувати]]
  • [[15. Як організувати комунікацію між мікросервісами]]