Open In App

Spring Boot - Circuit Breaker Pattern with Resilience4J

Last Updated : 30 Aug, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

The Circuit Breaker pattern is essential in microservices architecture to prevent cascading failures. When a service fails repeatedly due to network issues, unavailability, or other reasons, the Circuit Breaker opens to prevent additional requests from hitting the failing service. This approach helps maintain the system's overall health and provides fallback mechanisms for graceful degradation.

Resilience4J is a lightweight fault tolerance library, taking inspiration from Netflix Hystrix, but it is uniquely designed for modern Java applications, with strong support for functional programming It supports various resilience patterns, including Circuit Breaker, Rate Limiter, Bulkhead, and Retry.

Circuit Breaker with Resilience4J

The Circuit Breaker pattern is key to building resilient microservices. It acts as a fail-safe mechanism that prevents repeated failures in service calls by "breaking the circuit" and stopping the flow of requests when a service becomes unresponsive or fails consistently. This approach allows the system to fail fast and recover gracefully.

How the Circuit Breaker Works

  • Closed State: The circuit is closed and allows requests to pass through. If failures occur and reach the threshold, the circuit opens.
  • Open State: No requests can pass through. After a timeout, the circuit transitions to the half-open state.
  • Half-Open State: A restricted number of requests are allowed to pass through. If they succeed, the circuit returns to the closed state; if they fail, it returns to the open state.

Resilience4J's Circuit Breaker monitors calls to the downstream service, tracks failure rates, and trips the circuit if the failure rate crosses the defined threshold.

Why Use the Circuit Breaker Pattern?

  • Prevents Cascading Failures: Isolates failures to prevent a chain reaction that could bring down multiple services.
  • Graceful Degradation: Provides fallback responses to ensure a better user experience when failures occur.
  • Improves System Stability: Prevents continuous attempts to execute failing operations, thus improving overall stability.

How Resilience4J Implements the Circuit Breaker

Resilience4J provides a comprehensive and flexible way to implement the Circuit Breaker pattern in Spring Boot applications. It monitors downstream service calls and automatically trips the circuit if the failure rate exceeds the defined threshold. By using annotations like @CircuitBreaker and defining properties in the application.properties or application.yml file, developers can control the behavior of the Circuit Breaker.

Configuration Parameters in Resilience4J

  • failureRateThreshold: Sets the failure rate threshold percentage. When the failure rate meets or exceeds this percentage, the circuit transitions to the open state.
  • slidingWindowSize: Defines the size of the sliding window used for calculating the failure rate. It can be count-based or time-based.
  • minimumNumberOfCalls: Specifies the minimum number of calls needed before the Circuit Breaker begins calculating the failure rate. The Circuit Breaker will not open until this number is reached.
  • waitDurationInOpenState: Duration the Circuit Breaker stays in the open state before transitioning to the half-open state.
  • permittedNumberOfCallsInHalfOpenState: Number of test calls allowed in the half-open state to determine if the Circuit Breaker should close again.

Implementing the Circuit Breaker Pattern with Resilience4J in Spring Boot

In this example, we’ll create a simple Spring Boot application to demonstrate the use of the Circuit Breaker pattern using Resilience4J. We’ll configure the Circuit Breaker to handle failures gracefully and provide fallback responses when the service fails.

Step 1: Create a New Spring Boot Project

Create a new Spring Boot project using IntelliJ IDEA with the following options:

  • Name: spring-boot-circuit-breaker
  • Language: Java
  • Type: Maven
  • Packaging: Jar

Click on the Next button.

Project Metadata

Step 2: Add Dependencies

Add the following dependencies into the Spring Boot project.

  • Spring Web
  • Resilience4J
  • Spring Boot DevTools
  • Lombok

Click on the Create button.

Add Dependencies

Project Structure

The file structure should look like this:

Project Folder Structure

Step 3: Configure Application Properties

Rename application.properties to application.yml and add the following configuration:

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

management:
  endpoints:
    web:
      exposure:
        include: '*'

Step 4: Create the SampleService Class

Create the SampleService class to simulate the external service call that might fails of the Spring Boot application.

SampleService.java

Java
package com.gfg.springbootcircuitbreaker;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;

/**
 * Service class to simulate external service calls that might fail.
 */
@Service
public class SampleService {

    private static final String SAMPLE_SERVICE = "sampleService";

    /**
     * Simulates a service call that might fail.
     * @return Response from the external service or throws an exception
     */
    @CircuitBreaker(name = SAMPLE_SERVICE, fallbackMethod = "fallbackResponse")
    public String callExternalService() {
        // Simulating a random failure
        if (Math.random() > 0.5) {
            throw new RuntimeException("Service failed");
        }
        return "Service call succeeded";
    }

    /**
     * Fallback method called when the circuit breaker is open.
     * @param ex Exception thrown
     * @return Fallback response
     */
    public String fallbackResponse(Exception ex) {
        return "Fallback response: " + ex.getMessage();
    }
}

Explaination:

  • @CircuitBreaker(name = SAMPLE_SERVICE, fallbackMethod = "fallbackResponse"): This annotation marks the callExternalService method to be protected by the circuit breaker named as the SampleService. If this method failes due to the exception then it will call the fallbackResponse method.
  • fallbackResponse(Exception ex): This method can be defined as the fallback, which is called when the circuit breaker is open. It can provides the simple fallback response to indicate the failure gracefully.

Step 5: Create the CircuitBreakerController Class

Create the CircuitBreakerController class to define the REST endpoints for the testing the circuit breaker of the Spring Boot application.

CircuitBreakerController.java

Java
package com.gfg.springbootcircuitbreaker;

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

/**
 * Controller class to define REST endpoints for testing the Circuit Breaker.
 */
@RestController
public class CircuitBreakerController {

    private final SampleService sampleService;

    public CircuitBreakerController(SampleService sampleService) {
        this.sampleService = sampleService;
    }

    /**
     * Endpoint to test the Circuit Breaker.
     * @return Response from SampleService
     */
    @GetMapping("/test")
    public String testCircuitBreaker() {
        return sampleService.callExternalService();
    }
}
  • @RestController: Defines a RESTful controller.
  • @GetMapping("/test"): Endpoint for testing the Circuit Breaker functionality.

Step 6: Main Class

This is the entry point of the application. There is no changes are required in the main class.

Java
package com.gfg.springbootcircuitbreaker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Main class to run the Spring Boot application.
 */
@SpringBootApplication
public class SpringBootCircuitBreakerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootCircuitBreakerApplication.class, args);
    }
}

pom.xml file:

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gfg</groupId>
    <artifactId>spring-boot-circuit-breaker</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-circuit-breaker</name>
    <description>spring-boot-circuit-breaker</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2023.0.3</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
  • spring-boot-starter-web: Provides web application support.
  • resilience4j-spring-boot2: Integrates Resilience4J with Spring Boot.
  • spring-boot-starter-actuator: Provides production-ready features like monitoring and management.
  • lombok: Simplifies boilerplate code.

Step 7: Run the Application

To run the application, execute the following command:

mvn spring-boot:run

Output:

Application Starts

Step 8: Testing the Application

Use Postman or any REST client to test the /test endpoint:

GET http://localhost:8080/test
  • On a successful service call, you should receive "Service call succeeded".
  • If the service fails, you should see the fallback response "Fallback response: Service failed".
Postman UI

Application Logs

In the application logs, it will show the exception of the Spring Boot application.

Application Logs

Conclusion

In this article, we demonstrated how to implement the Circuit Breaker pattern using Resilience4J in a Spring Boot application. We configured the Circuit Breaker in the application.yml file, implemented a service with a Circuit Breaker, and tested the functionality through a REST endpoint. This approach helps build resilient applications by managing failures effectively and providing fallback mechanisms.


Next Article

Similar Reads