Circuit Breaker
Definition

States Explained

Implementation
// USING RESILIENCE4J
@Configuration
public class CircuitBreakerConfig {
@Bean
public CircuitBreakerRegistry circuitBreakerRegistry() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 50% failure rate trips
.waitDurationInOpenState(Duration.ofSeconds(30)) // Wait before half-open
.permittedNumberOfCallsInHalfOpenState(3) // Test calls
.slidingWindowSize(10) // Last 10 calls
.slidingWindowType(SlidingWindowType.COUNT_BASED)
.build();
return CircuitBreakerRegistry.of(config);
}
}
@Service
public class PaymentService {
private final CircuitBreaker circuitBreaker;
private final PaymentGateway gateway;
public PaymentService(CircuitBreakerRegistry registry,
PaymentGateway gateway) {
this.circuitBreaker = registry.circuitBreaker("payment");
this.gateway = gateway;
}
public PaymentResult processPayment(Payment payment) {
return circuitBreaker.executeSupplier(() ->
gateway.charge(payment)
);
}
// With fallback
public PaymentResult processPaymentWithFallback(Payment payment) {
return Try.ofSupplier(
CircuitBreaker.decorateSupplier(circuitBreaker,
() -> gateway.charge(payment)))
.recover(CallNotPermittedException.class,
e -> PaymentResult.pending("Service temporarily unavailable"))
.recover(Exception.class,
e -> PaymentResult.failed(e.getMessage()))
.get();
}
}
// ANNOTATION-BASED (Spring)
@Service
public class PaymentService {
@CircuitBreaker(name = "payment", fallbackMethod = "paymentFallback")
public PaymentResult processPayment(Payment payment) {
return gateway.charge(payment);
}
public PaymentResult paymentFallback(Payment payment, Exception e) {
log.warn("Payment circuit breaker fallback", e);
return PaymentResult.pending("Please try again later");
}
}
Configuration
# application.yml (Resilience4j)
resilience4j:
circuitbreaker:
instances:
payment:
# When to open
failureRateThreshold: 50
slowCallRateThreshold: 100
slowCallDurationThreshold: 2s
# How long to stay open
waitDurationInOpenState: 30s
# Half-open behavior
permittedNumberOfCallsInHalfOpenState: 3
# Sliding window
slidingWindowType: COUNT_BASED
slidingWindowSize: 10
minimumNumberOfCalls: 5
# What counts as failure
recordExceptions:
- java.io.IOException
- java.util.concurrent.TimeoutException
ignoreExceptions:
- com.example.BusinessException
# Often combined with other patterns
retry:
instances:
payment:
maxAttempts: 3
waitDuration: 500ms
retryExceptions:
- java.io.IOException
bulkhead:
instances:
payment:
maxConcurrentCalls: 10
maxWaitDuration: 100ms
timelimiter:
instances:
payment:
timeoutDuration: 3s
Fallback Strategies
// FALLBACK STRATEGIES
// 1. Return cached data
@CircuitBreaker(name = "products", fallbackMethod = "getCachedProducts")
public List<Product> getProducts() {
return productService.fetchAll();
}
public List<Product> getCachedProducts(Exception e) {
return cache.get("products", List.class);
}
// 2. Return default/degraded response
@CircuitBreaker(name = "recommendations", fallbackMethod = "getDefaultRecs")
public List<Product> getRecommendations(String userId) {
return recommendationService.getPersonalized(userId);
}
public List<Product> getDefaultRecs(String userId, Exception e) {
return productService.getTopSelling(10); // Generic recommendations
}
// 3. Queue for later processing
@CircuitBreaker(name = "notifications", fallbackMethod = "queueNotification")
public void sendNotification(Notification notification) {
notificationService.send(notification);
}
public void queueNotification(Notification notification, Exception e) {
messageQueue.enqueue("pending-notifications", notification);
log.warn("Notification queued for later delivery");
}
// 4. Return error with guidance
@CircuitBreaker(name = "checkout", fallbackMethod = "checkoutFallback")
public Order checkout(Cart cart) {
return orderService.createOrder(cart);
}
public Order checkoutFallback(Cart cart, Exception e) {
throw new ServiceUnavailableException(
"Checkout is temporarily unavailable. " +
"Your cart has been saved. Please try again in a few minutes.");
}
Tips & Tricks
