Circuit Breaker in Spring Boot

Annotate the Call

@Service
@RequiredArgsConstructor
@Slf4j
public class ProductCatalog {

    private final ProductClient productClient;
    private final ProductCache cache;

    @CircuitBreaker(name = "productService", fallbackMethod = "cachedProduct")
    public ProductDto getProduct(Long id) {
        ProductDto product = productClient.getProduct(id);
        cache.put(product);
        return product;
    }

    // same parameters + a Throwable at the end, same return type
    ProductDto cachedProduct(Long id, Throwable ex) {
        log.warn("productService fallback for {}: {}", id, ex.getClass().getSimpleName());
        return cache.find(id).orElseThrow(() -> new ServiceUnavailableException("product-service down"));
    }
}

Note: The annotations work through a Spring proxy, so calling getProduct from another method in the same class skips the circuit breaker. Call it from another bean.

Configure It

application.yml

application.yml

resilience4j:
  circuitbreaker:
    configs:
      default:
        sliding-window-type: COUNT_BASED      # or TIME_BASED (last N seconds)
        sliding-window-size: 10
        minimum-number-of-calls: 5            # do not judge on 1-2 calls
        failure-rate-threshold: 50            # %
        slow-call-duration-threshold: 2s      # slow calls count as bad too
        slow-call-rate-threshold: 80
        wait-duration-in-open-state: 10s
        permitted-number-of-calls-in-half-open-state: 3
        automatic-transition-from-open-to-half-open-enabled: true
        register-health-indicator: true
    instances:
      productService:
        base-config: default
        ignore-exceptions:                    # business errors are not failures
          - com.shopease.order.client.ProductNotFoundException
      paymentService:
        base-config: default
        wait-duration-in-open-state: 30s

management:
  health:
    circuitbreakers:
      enabled: true
  endpoints:
    web:
      exposure:
        include: health,circuitbreakers,circuitbreakerevents

Example output

$ curl -s localhost:8082/actuator/circuitbreakers
{"circuitBreakers":{"productService":{"failureRate":"60.0%","slowCallRate":"0.0%",
 "failureRateThreshold":"50.0%","slowCallRateThreshold":"80.0%","bufferedCalls":5,
 "failedCalls":3,"slowCalls":0,"slowFailedCalls":0,"notPermittedCalls":4,"state":"OPEN"}}}

# order-service log
WARN  c.s.order.ProductCatalog : productService fallback for 7: CallNotPermittedException

Important Settings

PropertyMeaningTypical
sliding-window-sizeHow many recent calls to look at10–100
minimum-number-of-callsNo decision before this many calls5–20
failure-rate-threshold% of failed calls that opens the circuit50
slow-call-duration-thresholdA call slower than this counts as slowp99 latency × 2
wait-duration-in-open-stateHow long to stay OPEN10–60 s
permitted-number-of-calls-in-half-open-stateTrial calls3–10
record-exceptions / ignore-exceptionsWhich errors countIgnore 4xx business errors