Implementing Circuit Breaker Pattern in Spring Boot using Resilience4j

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: 8081
spring:
application:
name: spring-boot-resiliency4j
resilience4j:
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

  1. Call the endpoint:
GET /active
  1. Simulate failures from the upstream API.
  2. Once the failure rate exceeds 50%, the circuit breaker will open.
  3. 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

FeatureTime BasedCount Based
MeasurementTime durationNumber of requests
Use caseReal-time trafficLow-traffic services
ExampleLast 10 secondsLast 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