Introduction :
In the world of microservices, reliability and resilience are paramount. Microservices often depend on other services to function, and these dependencies can lead to cascading failures if one service goes down or becomes slow. To mitigate these risks, the Circuit Breaker pattern is widely used. In this blog, we’ll explore how to implement this pattern in Spring Boot microservices using Resilience4j.
Prerequisites :
- JDK 11 or later
- Maven or Gradle
- Spring Boot Development Environment (here IntelliJ IDEA)
What is the Circuit Breaker Pattern :
The Circuit Breaker pattern is inspired by electrical circuit breakers that prevent overloads by breaking the circuit. Similarly, in software, a circuit breaker prevents a service from making requests that are likely to fail. Instead of allowing a failing service to affect the entire system, the circuit breaker “opens” to stop requests and returns a fallback response or error message.
The circuit breaker has three states :
Closed : Requests pass through normally., both services which are interacting are up and running, circuit breaker is CLOSED. If the number of failures crosses a threshold, the circuit breaker transitions to the Open state.
Open : Requests are not allowed, and a fallback response is returned. After a timeout period, the circuit breaker transitions to the Half-Open state. Calling another micro service will fail so an exception will be returned. That means, the flow is interrupted.
Half-Open : A few requests are allowed to pass through. If these requests succeed, the circuit breaker returns to the Closed state. If they fail, the circuit breaker returns to the Open state. In this state, only a LIMITED number of remote API calls are allowed to pass through. If the failing calls count is greater than this limited number, breaker turns again into OPEN state. Otherwise it is CLOSED.
Why Resilience4j :
Resilience4j is a lightweight, easy-to-use library for implementing various fault tolerance patterns, including circuit breakers, in Java and Spring Boot applications. Unlike other libraries, Resilience4j is designed for functional programming and provides greater control over how these patterns are implemented.
Setting Up Spring Boot Application :
Create 2 Microservices.I’m going to implement a simple inter service communication scenario using two services called course-service and student-service.
Scenario :
I want to retrieve student details along with their enrolled course details using two microservices, course-service and student-service
student-service : This service will manage student-related data, such as student profiles and their enrollment details in courses. It will communicate with course-service to fetch details about the courses in which a student is enrolled.
course-service : This service will handle all the course-related data, such as course descriptions, instructors, and status.
Step 1: Add Resilience4j Dependencies
Add the following dependencies to your service: Here I added dependency in student-services:
org.springframework.cloud
spring-cloud-starter-circuitbreaker-resilience4j
Step 2: Configure Application Properties
configure your circuit breaker in application.properties:
#Resilinece4j Properties
spring.cloud.circuitbreaker.resilience4j.cb.Course-Service.registerHealthIndicator=true
spring.cloud.circuitbreaker.resilience4j.cb.Course-Service.failureRateThreshold=50
spring.cloud.circuitbreaker.resilience4j.cb.Course-Service.waitDurationInOpenState=10000
spring.cloud.circuitbreaker.resilience4j.cb.Course-Service.permittedNumberOfCallsInHalfOpenState=10
spring.cloud.circuitbreaker.resilience4j.cb.Course-Service.slidingWindowSize=100
spring.cloud.circuitbreaker.resilience4j.cb.Course-Service.minimumNumberOfCalls=5
spring.cloud.circuitbreaker.resilience4j.cb.Course-Service.automaticTransitionFromOpenToHalfOpenEnabled=100
Here’s a breakdown of the configuration :
- slidingWindowSize : The number of calls to evaluate when determining the failure rate.
- minimumNumberOfCalls : The minimum number of calls required before the circuit breaker can calculate a failure rate.
- failureRateThreshold : The percentage of calls that must fail to trigger the circuit breaker.
- waitDurationInOpenState : The time the circuit breaker will remain open before transitioning to half-open.
- permittedNumberOfCallsInHalfOpenState : The number of calls allowed when the circuit breaker is in a half-open state.
- automaticTransitionFromOpenToHalfOpenEnabled : Automatically transition from open to half-open state.
Step 3: Implement Circuit Breaker in Your Service
Apply the circuit breaker to a method in your service layer using the @CircuitBreaker annotation:
@CircuitBreaker(name = "course", fallbackMethod = "fallbackMethod")
public StudentCourseDetailsResponse getStudentWithTheirCourseDetail(Long id) {
StudentCourseDetailsResponse studentCourseDetailsResponse=new StudentCourseDetailsResponse();
Course course = courseServiceFeignClient.getCourseDetails(id);
Student student=studentRepo.findById(id).orElseThrow(null);
studentCourseDetailsResponse.setCourse(course);
studentCourseDetailsResponse.setStudent(student);
return studentCourseDetailsResponse;
}
public StudentCourseDetailsResponse fallbackMethod(Long id, Throwable throwable) {
StudentCourseDetailsResponse studentCourseDetailsResponse=new StudentCourseDetailsResponse();
return studentCourseDetailsResponse;
}
- The @CircuitBreaker annotation applies the circuit breaker to the callExternalService method.
- The name attribute refers to the circuit breaker instance configured in application.propreties.
- The fallbackMethod specifies the method to call when the circuit breaker is open.
Step 4: Define controller.
Define the endpoint in the controller that will handle the response.
@GetMapping(value = "/get-student-course/{id}")
public ResponseEntity getStudentWithTheirCourseDetail(@PathVariable Long id) {
try {
StudentCourseDetailsResponse user = userServices.getStudentWithTheirCourseDetail(id);
return new ResponseEntity<>(new Response(user,"student with course detail retrive", Boolean.TRUE), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(new Response("Error retrive details", Boolean.FALSE),
HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Here it will handle the request to retrieve student details along with their enrolled course details. This endpoint will utilize the Feign client to communicate with course-service. If course-service is unavailable, the fallback method defined earlier will be invoked, providing a default or error response.
Step 5: Monitoring and Metrics:
Resilience4j integrates seamlessly with Spring Boot Actuator. You can monitor circuit breakers by enabling Actuator:
org.springframework.boot
spring-boot-starter-actuator
Now, access metrics and health indicators via Actuator endpoints, such as /actuator/metrics and /actuator/health.
Step 6: Testing the Circuit Breaker
To test the circuit breaker, trigger failures in your external service. For example, shut down the external service, and observe how the circuit breaker transitions from closed to open, and then to half-open
Conclusion:
By implementing the Circuit Breaker pattern with Resilience4j in your Spring Boot microservices, you can build resilient and fault-tolerant systems. This approach ensures that a failure in one service doesn’t cascade and cause widespread issues across your entire application. With the proper configuration and monitoring in place, you can maintain high availability and reliability in your microservices architecture.There are other resilience patterns offered by Resilience4j, such as Rate Limiter, Retry, and Bulkhead.