Spring Cloud Circuit Breaker – Podstawy i Implementacja

TL;DR: Spring Cloud Circuit Breaker to implementacja wzorca Circuit Breaker dla microservices. Chroni aplikację przed kaskadowymi awariami poprzez monitorowanie wywołań do zewnętrznych usług i tymczasowe ich przerywanie gdy zaczynają zawodzić. Wspiera Hystrix i Resilience4j jako implementacje.

Dlaczego Spring Cloud Circuit Breaker jest ważny?

W architekturze microservices aplikacje są ze sobą ściśle powiązane przez wywołania HTTP. Gdy jedna usługa zaczyna działać wolno lub zawodzi, może to spowodować kaskadową awarię całego systemu. Circuit Breaker działa jak bezpiecznik elektryczny – gdy wykryje problemy, „otwiera obwód” i zapobiega dalszym wywołaniom do uszkodzonej usługi, dając jej czas na regenerację.

Z perspektywy biznesowej oznacza to większą stabilność aplikacji, lepsze doświadczenie użytkownika i mniej nocnych telefonów od operations team.

Co się nauczysz:

  • Jak działa wzorzec Circuit Breaker w microservices
  • Konfigurację Spring Cloud Circuit Breaker z Hystrix
  • Implementację fallback methods dla awaryjnych scenariuszy
  • Monitorowanie i dashboardy dla Circuit Breaker
  • Najlepsze praktyki w środowisku produkcyjnym

Wymagania wstępne:

  • Znajomość Spring Boot (6+ miesięcy)
  • Podstawy REST API i HTTP
  • Pojęcie o architekturze microservices
  • Java 8+ i Maven

Czym jest Circuit Breaker?

Circuit Breaker – wzorzec projektowy który monitoruje wywołania do zewnętrznych zasobów i „otwiera obwód” gdy liczba błędów przekroczy określony próg, zapobiegając dalszym wywołaniom przez określony czas.

Circuit Breaker ma三 stany:
– **CLOSED** – normalne działanie, wywołania przechodzą
– **OPEN** – obwód otwarty, wszystkie wywołania są odrzucane
– **HALF-OPEN** – testowy stan, przepuszcza ograniczoną liczbę wywołań

Analogia: Circuit Breaker to jak bezpiecznik w domu. Gdy prąd jest za duży, bezpiecznik się „wyłącza” chroniąc instalację. Po pewnym czasie możesz spróbować go z powrotem włączyć.

Konfiguracja Spring Cloud Circuit Breaker

Najpierw dodaj dependency do pom.xml:


    org.springframework.cloud
    spring-cloud-starter-netflix-hystrix


    org.springframework.cloud
    spring-cloud-starter-netflix-hystrix-dashboard

Włącz Circuit Breaker w głównej klasie aplikacji:

@SpringBootApplication
@EnableCircuitBreaker
@EnableHystrixDashboard
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}
Informacja: @EnableCircuitBreaker automatycznie konfiguruje Hystrix jako domyślną implementację Circuit Breaker w Spring Cloud.

Implementacja Circuit Breaker

Oto przykład serwisu który wywołuje zewnętrzny API z Circuit Breaker:

@Service
public class PaymentService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    @HystrixCommand(
        fallbackMethod = "getPaymentFallback",
        commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000")
        }
    )
    public PaymentResult processPayment(PaymentRequest request) {
        String url = "http://payment-service/api/payments";
        return restTemplate.postForObject(url, request, PaymentResult.class);
    }
    
    // Fallback method - wywołana gdy Circuit Breaker jest OPEN
    public PaymentResult getPaymentFallback(PaymentRequest request) {
        return PaymentResult.builder()
            .status("DEFERRED")
            .message("Payment service temporarily unavailable. Transaction queued for later processing.")
            .transactionId(UUID.randomUUID().toString())
            .build();
    }
}
Pro tip: Fallback method musi mieć identyczną sygnaturę co oryginalna metoda. Może zwracać domyślne wartości, dane z cache lub kolejkować żądanie do późniejszego przetworzenia.

Konfiguracja parametrów Circuit Breaker

Kluczowe parametry Hystrix w application.yml:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
      circuitBreaker:
        requestVolumeThreshold: 10      # Min liczba requestów w oknie
        errorThresholdPercentage: 50    # Procent błędów powodujący otwarcie
        sleepWindowInMilliseconds: 10000 # Czas oczekiwania w stanie OPEN
      metrics:
        rollingStats:
          timeInMilliseconds: 20000     # Okno czasowe dla statystyk
ParametrOpisDomyślna wartość
requestVolumeThresholdMin. liczba requestów w oknie czasowym20
errorThresholdPercentageProcent błędów powodujący otwarcie50%
sleepWindowInMillisecondsCzas w stanie OPEN5000ms
timeoutInMillisecondsTimeout dla pojedynczego wywołania1000ms
Uwaga: Zbyt niski requestVolumeThreshold może powodować przedwczesne otwieranie obwodu. Zbyt wysoki errorThresholdPercentage może nie chronić przed awarią.

Monitorowanie z Hystrix Dashboard

Hystrix Dashboard pozwala monitorować stan Circuit Breaker w czasie rzeczywistym:

@RestController
public class HystrixStreamController {
    
    @RequestMapping("/health")
    public String health() {
        return "OK";
    }
}

Konfiguracja w application.yml:

management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
  endpoint:
    hystrix:
      stream:
        enabled: true

Dashboard będzie dostępny pod: http://localhost:8080/hystrix

Najlepsze praktyki

Pro tip: Zawsze implementuj sensowne fallback methods. Pusty fallback to stracona szansa na graceful degradation.

### Fallback strategies:
1. **Cache fallback** – zwróć dane z local cache
2. **Default values** – ustaw rozsądne wartości domyślne
3. **Queue for later** – odłóż żądanie do kolejki
4. **Alternative service** – użyj backup service

@Service 
public class UserService {
    
    @Autowired
    private UserCache userCache;
    
    @HystrixCommand(fallbackMethod = "getUserFromCache")
    public User getUser(Long userId) {
        return restTemplate.getForObject("/api/users/" + userId, User.class);
    }
    
    public User getUserFromCache(Long userId) {
        User cachedUser = userCache.get(userId);
        if (cachedUser != null) {
            return cachedUser;
        }
        
        // Zwróć podstawowe informacje gdy brak cache
        return User.builder()
            .id(userId)
            .name("User " + userId)
            .status("UNKNOWN_STATUS")
            .build();
    }
}
Typowy błąd: Nie testowanie fallback methods. Pamiętaj że fallback może być wywołany w produkcji i musi działać poprawnie.

Testing Circuit Breaker

Test jednostkowy Circuit Breaker z mockowanym service:

@RunWith(SpringRunner.class)
@SpringBootTest
public class PaymentServiceTest {
    
    @Autowired
    private PaymentService paymentService;
    
    @MockBean
    private RestTemplate restTemplate;
    
    @Test
    public void shouldCallFallbackWhenServiceFails() {
        // Given
        PaymentRequest request = new PaymentRequest("123", 100.0);
        when(restTemplate.postForObject(anyString(), any(), eq(PaymentResult.class)))
            .thenThrow(new RuntimeException("Service unavailable"));
        
        // When
        PaymentResult result = paymentService.processPayment(request);
        
        // Then
        assertThat(result.getStatus()).isEqualTo("DEFERRED");
        assertThat(result.getMessage()).contains("temporarily unavailable");
    }
}
Kiedy Circuit Breaker przechodzi do stanu OPEN?

Circuit Breaker otwiera się gdy procent błędów w oknie czasowym przekroczy próg (domyślnie 50%) i liczba requestów przekroczy minimum (domyślnie 20 wywołań).

Czy mogę używać Circuit Breaker z async calls?

Tak, Hystrix obsługuje wywołania asynchroniczne przez @HystrixCommand z commandKey i obsługę CompletableFuture.

Co to jest „bulkhead pattern” w Hystrix?

Bulkhead pattern izoluje różne wywołania używając oddzielnych thread pools, aby awaria jednego service nie wpływała na inne.

Jak ustawić różne timeouty dla różnych services?

Użyj commandKey w @HystrixCommand i skonfiguruj osobne właściwości dla każdego klucza w application.yml.

Czy Circuit Breaker wpływa na performance?

Hystrix dodaje ~1-2ms overhead per wywołanie, ale zapobiega kaskadowym awariom które kosztowałyby znacznie więcej.

Jak debugować problemy z Circuit Breaker?

Użyj Hystrix Dashboard, logi aplikacji i metryki. Sprawdź czy parametry (thresholds, timeouts) są odpowiednio skonfigurowane.

Przydatne zasoby:

🚀 Zadanie dla Ciebie

Stwórz prostą aplikację z dwoma microservices: OrderService i PaymentService. Zaimplementuj Circuit Breaker w OrderService który wywołuje PaymentService. Przetestuj różne scenariusze awarii i zobacz jak działa fallback. Skonfiguruj Hystrix Dashboard i obserwuj metryki w czasie rzeczywistym.

Masz pytania o implementację Circuit Breaker w Twoim projekcie? Podziel się swoimi doświadczeniami w komentarzach!

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewijanie do góry