avatarVinotech

Summary

The provided content is a comprehensive guide on implementing the Circuit Breaker pattern in a Spring Boot application using the Resilience4j library to enhance fault tolerance and system reliability.

Abstract

The article offers a step-by-step tutorial on how to integrate Resilience4j, a fault tolerance library designed for Java 8 and functional programming, into a Spring Boot application. It emphasizes the importance of fault tolerance in microservices architecture, detailing patterns like Circuit Breaker, Retry, Fallback, Bulkhead, and Timeouts to handle potential faults. The guide includes a practical example using a Student Service that fetches course details from a Course Service, demonstrating how to configure and use Resilience4j's circuit breaker to prevent repeated failures and maintain system availability. It covers project setup, application configuration, service layer implementation, and controller layer integration, along with testing procedures to verify both successful operations and fallback mechanisms when the Course Service is unavailable.

Opinions

  • The author advocates for the use of Resilience4j over Netflix Hystrix due to its lightweight nature and suitability for Java 8 and functional programming.
  • The article suggests that implementing fault tolerance mechanisms like the Circuit Breaker pattern is essential for maintaining availability and reliability in microservices architectures.
  • The author implies that proper fault tolerance can improve user experience by ensuring the application continues to serve requests despite some functionality being degraded.
  • The use of a sliding window for measuring failure rates and configurable parameters for circuit breaker states is presented as a flexible approach to managing faults.
  • The author emphasizes the importance of monitoring the health status of circuit breakers, which can be achieved through integration with Spring Boot Actuator.
  • Testing both success and failure scenarios is highlighted as a critical step in verifying the correct implementation of the Circuit Breaker pattern.

Spring Boot Circuit Breaker Example with Resilience4j: Step-by-Step Guide

How to Implement and Test Circuit Breaker Pattern in Spring Boot Using Resilience4j — Includes Tests for Success and Failure

When developing an application, particularly one based on microservices, it’s common to encounter issues during real-time execution. These might include slow response times, network failures, REST call failures, or issues arising from a high volume of requests. To handle such potential faults, it’s essential to implement a fault tolerance mechanism within the application. Resilience4j is a library that can be used to achieve this.

Resilience4j is a lightweight, easy-to-use fault tolerance library inspired by Netflix Hystrix but specifically designed for Java 8 and functional programming. This article will focus on how to implement fault tolerance in microservices using Resilience4j.

What is Fault Tolerance in Microservices?

Fault tolerance in microservices refers to the system’s ability to continue functioning even when one or more of its components fail. Microservices architectures often consist of many small, independent services that communicate with each other over a network, making them more prone to failures. Ensuring fault tolerance helps maintain availability and reliability despite these failures.

Several patterns help achieve fault tolerance in microservices:

  • Circuit Breaker Pattern: Protects services from repeated failure by “opening the circuit” when failures exceed a threshold, thus preventing further calls to the failing service for a while.
  • Retry Pattern: Automatically retries failed requests in case the failure was transient.
  • Fallback Pattern: Provides alternative methods or static data when a service fails or is unavailable.
  • Bulkhead Pattern: Isolates different parts of the system into separate pools, preventing one service’s failure from affecting others.
  • Timeouts: Limits the time a service waits for a response, preventing long delays from unresponsive services.

Importance of Fault Tolerance:

  • Minimizes Downtime: Even when services fail, the system remains partially operational, minimizing the impact on users.
  • Improves User Experience: Ensures the application continues to serve requests, even if some functionality is degraded.
  • Enhances Scalability: With proper fault tolerance, services can handle dynamic changes in load and fail gracefully.

Here’s a complete example of the Circuit Breaker Pattern using Resilience4j with Spring Boot, including a diagram, use case scenario, input/output.

1. Use Case Scenario

In this example, the Student Service fetches course details from the Course Service. The Course Service might become unavailable due to network issues or downtime. Using the Circuit Breaker Pattern ensures that if the Course Service fails too many times within a short period, the calls to that service will be automatically blocked (open circuit) for a while before allowing them again (half-open, then closed state).

2. Project Setup

Ensure you have the following dependencies in your pom.xml:

<dependencies>
    <dependency>
        <groupId>io.github.resilience4j</groupId>
        <artifactId>resilience4j-spring-boot2</artifactId>
        <version>1.7.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

3. Application Configuration

application.yml

resilience4j.circuitbreaker:
  instances:
    courseService:
      registerHealthIndicator: true
      slidingWindowSize: 5
      minimumNumberOfCalls: 3
      failureRateThreshold: 50
      waitDurationInOpenState: 10s
      permittedNumberOfCallsInHalfOpenState: 3

This configuration states:

  • The circuit breaker will be triggered after 3 failures.
  • 50% failure rate will open the circuit.
  • The circuit remains open for 10 seconds before going into the half-open state.

1. resilience4j.circuitbreaker.instances.courseService :

This part defines the circuit breaker instance for a specific service, in this case, courseService. Resilience4j allows you to create different circuit breakers for different services (or parts of your application).

Property Explanations:

2. registerHealthIndicator: true

  • This property indicates whether the circuit breaker will be registered as a health indicator.
  • When set to true, it allows the circuit breaker to expose its health status, which can be monitored through Spring Boot Actuator. This is useful for detecting whether the circuit is open, half-open, or closed.

3. slidingWindowSize: 5

  • This defines the size of the sliding window for measuring the failure rate of requests. In this case, it tracks the last 5 requests to decide whether the failure rate has exceeded the threshold.
  • The sliding window allows the circuit breaker to evaluate recent calls within this window size. If the failure rate within these calls exceeds the threshold, the circuit breaker will open.

4. minimumNumberOfCalls: 3

  • This specifies the minimum number of requests that must be made before the circuit breaker starts evaluating the failure rate. In this case, it waits for at least 3 calls before deciding whether to open the circuit.
  • Until this minimum number is met, the circuit breaker will not be triggered, even if there are failures.

5. failureRateThreshold: 50

  • The failure rate threshold is set to 50%, meaning that if 50% or more of the requests within the sliding window (last 5 requests) fail, the circuit breaker will open.
  • For example, if 2 out of 5 calls fail, the failure rate is 40%, so the circuit breaker remains closed. But if 3 out of 5 fail, the failure rate is 60%, and the circuit will open.

6. waitDurationInOpenState: 10s

  • This defines how long the circuit breaker will stay open before it transitions to the half-open state. In this case, the circuit remains open for 10 seconds.
  • When the circuit is open, all requests will automatically fail, and no further attempts will be made to call the failing service. After 10 seconds, it moves to the half-open state, where it tests the service again.

7. permittedNumberOfCallsInHalfOpenState: 3

  • When the circuit is in the half-open state, this property defines how many calls are allowed to pass through to test if the service is back to normal. In this case, 3 calls are allowed.
  • If these 3 calls succeed, the circuit will close, and normal traffic will resume. If they fail, the circuit will open again for another 10 seconds.

4. Service Layer Implementation

CourseService.java

package com.example.resilience4jdemo.service;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.HttpServerErrorException;

@Service
public class CourseService {

    private final RestTemplate restTemplate = new RestTemplate();

    @CircuitBreaker(name = "courseService", fallbackMethod = "fallbackCourseDetails")
    public String getCourseDetails(String studentId) {
        String url = "http://localhost:8081/course/" + studentId;
        return restTemplate.getForObject(url, String.class);
    }

    public String fallbackCourseDetails(String studentId, Throwable t) {
        return "Fallback: Course service is currently unavailable for student " + studentId;
    }
}

1. @CircuitBreaker Annotation

  • This annotation is used to mark a method for which the circuit breaker functionality should be applied.

2. name Attribute

  • name = "courseService"
  • This specifies the name of the circuit breaker instance. In this case, the circuit breaker is named "courseService".
  • The name corresponds to the configuration defined in your application.yml or application.properties file. It helps to identify which circuit breaker configuration to apply.

3. fallbackMethod Attribute

  • fallbackMethod = "fallbackCourseDetails"
  • This specifies the name of the method to be called if the circuit breaker is open or if the annotated method fails.
  • The fallback method should have the same parameters as the annotated method, with an additional Throwable parameter for capturing exceptions if needed.

StudentService.java

package com.example.resilience4jdemo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class StudentService {

    @Autowired
    private CourseService courseService;

    public String getStudentCourseDetails(String studentId) {
        return courseService.getCourseDetails(studentId);
    }
}

5. Controller Layer

StudentController.java

package com.example.resilience4jdemo.controller;

import com.example.resilience4jdemo.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    @GetMapping("/student/{id}/course")
    public String getStudentCourse(@PathVariable String id) {
        return studentService.getStudentCourseDetails(id);
    }
}

.

+--------------------+        +--------------------+
|                    |        |                    |
|   Student Service   |<------>+    Course Service  |
|                    |        |                    |
+--------------------+        +--------------------+
         |
         |
         v
+---------------------+
| Resilience4j        |
| Circuit Breaker     |
| (courseService)     |
+---------------------+

If you want to create a dummy response for the URL http://localhost:8081/course/{studentId}, here's how you can implement it in a Spring Boot service:

package com.example.courseservice.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CourseController {

    @GetMapping("/course/{studentId}")
    public String getCourseDetails(@PathVariable String studentId) {
        // Dummy response
        return "{ \"courseName\": \"Computer Science\", \"studentId\": \"" + studentId + "\", \"duration\": \"4 years\" }";
    }
}

You can test the service using Postman or curl to ensure it returns the response:

{
    "courseName": "Computer Science",
    "studentId": "123",
    "duration": "4 years"
}

6. Testing the Circuit Breaker

To test both success and failure scenarios using Postman, follow the steps below:

1. Start both services:

  • Student Service on port 8080.
  • Course Service on port 8081.

2. Success Scenario (Course Service is UP)

2.1 Request

2.2 Expected Response

When the Course Service is running and the student ID exists, you’ll get a normal response from the Course Service. The response might look like:

{
    "courseName": "Computer Science",
    "duration": "4 years"
}

This verifies that the Student Service successfully communicates with the Course Service.

3. Failure Scenario (Course Service is DOWN)

3.1 Stop the Course Service

Stop the Course Service running on port 8081.

3.2 Request

Make the same request as before:

3.3 Expected Response

Once the Course Service is down and the circuit breaker is triggered (after multiple failures based on your slidingWindowSize configuration), the fallback method will respond with:

{
    "message": "Fallback: Course service is currently unavailable for student 1"
}

This demonstrates the Circuit Breaker Pattern in action. After a certain number of failures, the circuit breaker will open, and the fallback method will be called, protecting the system from further stress.

4. Simulating Circuit Breaker States

Circuit Breaker states:

  1. Closed: Initially, all requests go through to the Course Service.
  2. Open: After the threshold is reached (e.g., 50% failure rate in the last 3 attempts), the circuit breaker opens, and the fallback is triggered immediately.
  3. Half-open: After a waiting period (waitDurationInOpenState), the circuit breaker allows some requests to pass through to test if the service is back.

To observe this behavior:

  • Stop the Course Service, make several requests until the circuit breaker opens.
  • Restart the Course Service after the wait duration (e.g., 10 seconds), and observe the half-open state where some requests pass through to the Course Service before the circuit returns to the closed state.

Postman History and Results:

  • Success: Normal response from the Course Service.
  • Failure: Fallback response with “Fallback: Course service is currently unavailable”.

This will show both the success and failure paths handled by the circuit breaker.

👏 If you found my articles useful, please consider giving it claps and sharing it with your friends and colleagues.

To read other topics

Circuit Breaker Pattern
Circuit Breaker
Resilience4j
Fault Tolerance
Recommended from ReadMedium