Circuit Breaker Pattern
In modern microservices architecture, services often depend on external APIs or other services. When those dependencies become slow or unavailable, they can cause cascading failures across the system.
One common resilience pattern to prevent this is the Circuit Breaker Pattern.
In this article, we will implement a Circuit Breaker in a Spring Boot application using Resilience4j.
What is the Circuit Breaker Pattern?
A Circuit Breaker protects your application from repeatedly calling a failing service.
It works similarly to an electrical circuit breaker.
The circuit has three states:
Closed
- Requests flow normally to the upstream service.
- Failures are monitored.
Open
- When failures exceed a threshold, the circuit opens.
- Requests fail immediately without calling the upstream service.
Half-Open
- After a waiting period, a few test requests are allowed.
- If they succeed → circuit closes.
- If they fail → circuit opens again.
This prevents your system from being overwhelmed when an upstream service is unhealthy.
Project Setup
Add the Resilience4j dependency to your Spring Boot project.
Maven
<dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-boot3</artifactId></dependency>
Circuit Breaker Configuration
We configure circuit breakers inside application.yaml.
server: port: 8081spring: application: name: spring-boot-resiliency4jresilience4j: circuitbreaker: instances: dummyjson-window: registerHealthIndicator: true slidingWindowType: TIME_BASED slidingWindowSize: 10 minimumNumberOfCalls: 2 permittedNumberOfCallsInHalfOpenState: 2 failureRateThreshold: 50 waitDurationInOpenState: 30s dummyjson-count: registerHealthIndicator: true slidingWindowType: COUNT_BASED slidingWindowSize: 10 minimumNumberOfCalls: 2 permittedNumberOfCallsInHalfOpenState: 2 failureRateThreshold: 50 waitDurationInOpenState: 30s
In this example we configured two circuit breakers:
dummyjson-window
- Uses a time-based sliding window
- Failures are calculated over a time period
dummyjson-count
- Uses a count-based sliding window
- Failures are calculated over a number of requests
REST Controller Implementation
Below is a sample controller that calls an upstream API and protects it with a circuit breaker.
@RestController@RequestMapping("/active")public class ActiveController { Logger logger = LoggerFactory.getLogger(ActiveController.class); private final RestTemplate restTemplate; @Autowired public ActiveController(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @GetMapping @CircuitBreaker(name = "dummyjson-window", fallbackMethod = "getActiveFallback") public ResponseEntity<String> getActive() { logger.info("Invoking upstream service from /active endpoint"); String url = "https://dummyjson.com/test"; HttpHeaders headers = new HttpHeaders(); HttpEntity<Void> requestEntity = new HttpEntity<>(headers); ResponseEntity<String> resp = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); return resp; } @GetMapping("/count") @CircuitBreaker(name = "dummyjson-count", fallbackMethod = "getActiveFallback") public ResponseEntity<String> getActiveCountBased() { logger.info("Invoking count-based upstream service from /active/count endpoint"); String url = "https://dummyjson.com/test"; HttpHeaders headers = new HttpHeaders(); HttpEntity<Void> requestEntity = new HttpEntity<>(headers); ResponseEntity<String> resp = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); return resp; } public ResponseEntity<String> getActiveFallback(Throwable t) { logger.error("Circuit breaker fallback invoked", t); return getLocal(); } @GetMapping("/local") public ResponseEntity<String> getLocal() { String body = "{ \"status\": \"up\", \"source\": \"local-fallback\" }"; HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json"); return ResponseEntity.ok().headers(headers).body(body); }}
How the Fallback Works
If the upstream API fails repeatedly and the circuit breaker opens:
Instead of calling the upstream API, the request is routed to the fallback method.
public ResponseEntity<String> getActiveFallback(Throwable t)
This fallback returns a local response:
{ "status": "up", "source": "local-fallback"}
This allows your application to continue functioning even when dependencies fail.
Testing the Circuit Breaker
- Call the endpoint:
GET /active
- Simulate failures from the upstream API.
- Once the failure rate exceeds 50%, the circuit breaker will open.
- All requests will start returning the fallback response instead of calling the upstream API.
After 30 seconds, the circuit will move to half-open and allow a few test requests.
Time-Based vs Count-Based Sliding Windows
| Feature | Time Based | Count Based |
|---|---|---|
| Measurement | Time duration | Number of requests |
| Use case | Real-time traffic | Low-traffic services |
| Example | Last 10 seconds | Last 10 requests |
Both strategies are supported in Resilience4j.
Final Thoughts
The Circuit Breaker pattern is essential for building resilient microservices.
By using Spring Boot with Resilience4j, we can easily:
- Prevent cascading failures
- Implement graceful fallbacks
- Improve system reliability
This approach is widely used in production systems that depend on external APIs.
Leave a comment