package com.example.demo.service;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import io.github.resilience4j.retry.annotation.Retry;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class ResilientService {
private static final Logger logger = LoggerFactory.getLogger(ResilientService.class);
private final RestTemplate restTemplate;
public ResilientService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@CircuitBreaker(name = "externalAPI", fallbackMethod = "fallbackResponse")
@Retry(name = "externalAPI")
public String callExternalAPI() {
logger.info("Calling external API");
return restTemplate.getForObject("https://api.example.com/data", String.class);
}
@RateLimiter(name = "userService")
public String rateLimitedOperation() {
logger.info("Executing rate limited operation");
return "Operation completed";
}
@Bulkhead(name = "processingService", type = Bulkhead.Type.THREADPOOL)
public String isolatedOperation() {
logger.info("Executing isolated operation");
// Heavy processing
return "Processing complete";
}
@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")
@Retry(name = "paymentService", fallbackMethod = "paymentFallback")
public String processPayment(String orderId) {
logger.info("Processing payment for order: {}", orderId);
// Call payment service
return "Payment successful";
}
// Fallback methods
private String fallbackResponse(Exception e) {
logger.error("Fallback triggered due to: {}", e.getMessage());
return "Service temporarily unavailable. Using cached data.";
}
private String paymentFallback(String orderId, Exception e) {
logger.error("Payment fallback for order {} due to: {}", orderId, e.getMessage());
return "Payment queued for processing";
}
}
resilience4j:
circuitbreaker:
instances:
externalAPI:
registerHealthIndicator: true
slidingWindowSize: 10
minimumNumberOfCalls: 5
permittedNumberOfCallsInHalfOpenState: 3
automaticTransitionFromOpenToHalfOpenEnabled: true
waitDurationInOpenState: 10s
failureRateThreshold: 50
slowCallRateThreshold: 100
slowCallDurationThreshold: 2s
paymentService:
slidingWindowSize: 20
failureRateThreshold: 60
waitDurationInOpenState: 20s
retry:
instances:
externalAPI:
maxAttempts: 3
waitDuration: 1s
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
retryExceptions:
- org.springframework.web.client.RestClientException
ignoreExceptions:
- java.lang.IllegalArgumentException
paymentService:
maxAttempts: 5
waitDuration: 2s
ratelimiter:
instances:
userService:
limitForPeriod: 100
limitRefreshPeriod: 1s
timeoutDuration: 0s
bulkhead:
instances:
processingService:
maxConcurrentCalls: 10
maxWaitDuration: 100ms
timelimiter:
instances:
externalAPI:
timeoutDuration: 5s
Resilience4j provides resilience patterns for fault tolerance. Circuit breakers prevent cascading failures—open after threshold failures, allow retry after timeout. Rate limiters control request rates. Retry mechanisms handle transient failures. Bulkheads isolate resources preventing exhaustion. Time limiters enforce execution deadlines. Fallback methods provide degraded functionality. I combine patterns for robust microservices. Metrics expose circuit states, failure rates. Spring Boot integration uses annotations—@CircuitBreaker, @RateLimiter, @Retry. Configuration tunes thresholds, timeouts, limits. Resilience4j replaces Netflix Hystrix with lighter, more flexible implementation. Proper resilience design prevents single points of failure and improves system availability under stress.