A Guide to Spring Cloud Circuit Breaker with Resilience4J

Spring Cloud Circuit Breaker provides an abstraction across different circuit breaker implementations. It provides a consistent API to use in your applications allowing you the developer to choose the circuit breaker implementation that best fits your needs for your app.

Resilience4j is a lightweight fault tolerance library designed for Java 8+ functional programming. Provides several fault tolerance mechanisms: CircuitBreaker, Retry, RateLimiter, TimeLimiter, and Bulkhead.

In this post, I will cover common uses of Spring Cloud Circuit Breaker with Resilience4J. This post is based on Java 21, Spring Boot 3.5.3, and Spring Cloud 2025.0.0.

1. Create Project and Add Dependencies

Using Gradle

$ mkdir spring-cloud-circuit-breaker && cd spring-cloud-circuit-breaker
$ gradle --configuration-cache \
init --use-defaults \
--type java-application \
--package com.taogen.springcloud

app/build.gradle.kts

plugins {
java
id("org.springframework.boot") version "3.5.3"
// Use the Spring Dependency Management plugin to manage Spring Cloud dependencies.
id("io.spring.dependency-management") version "1.1.7"
}

extra["springCloudVersion"] = "2025.0.0"

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

repositories {
mavenCentral()
}

dependencies {
implementation("org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j")
implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

dependencyManagement {
imports {
mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}")
}
}

tasks.withType<Test> {
useJUnitPlatform()
}

Using Maven

$ mvn -B archetype:generate \
-DgroupId=com.taogen.springcloud \
-DartifactId=spring-cloud-circuit-breaker \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.5

pom.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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.taogen.springcloud</groupId>
<artifactId>spring-cloud-circuit-breaker</artifactId>
<version>1.0-SNAPSHOT</version>

<name>spring-cloud-circuit-breaker</name>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>21</maven.compiler.release>
<java.version>21</java.version>
<spring-boot.version>3.5.3</spring-boot.version>
<spring-cloud.version>2025.0.0</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.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>
</plugin>
</plugins>
</build>
</project>

Using Spring Initializr

Go to https://start.spring.io/

Project Metadata

  • Group: com.taogen.springcloud
  • Artifact: spring-cloud-circuit-breaker

Add dependencies

  • Resilience4J
  • Eureka Discovery Client
  • Spring Web
  • Spring Boot Actuator

2. Spring Configuration

The spring configuration mainly contains Eureka client configurations and resilience4j configurations.

application.yml

spring:
application:
name: spring-cloud-circuit-breaker

# Eureka client
eureka:
instance:
preferIpAddress: true
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:8761/eureka/
healthcheck:
enabled: true
server:
# configure the service to use a random port number
port: 0

# Actuator
management:
endpoints:
web:
exposure:
include: health,info,beans

# resilience4j
resilience4j.circuitbreaker:
instances:
circuit-breaker-resilience4j-2:
registerHealthIndicator: true
# the resilience4j configuration values are just for demostration
slidingWindowSize: 6
waitDurationInOpenState: 10s
failureRateThreshold: 60
  • slidingWindowSize: Configures the size of the sliding window, which is used to record the outcome of calls when the CircuitBreaker is closed.
  • waitDurationInOpenState: The time that the CircuitBreaker should wait before transitioning from open to half-open.
  • failureRateThreshold: Configures the failure rate threshold in percentage. When the failure rate is equal to or greater than the threshold, the CircuitBreaker transitions to open and starts short-circuiting calls.

When the sliding window is full and the failure rate is equal to or greater than failureRateThreshold, the CircuitBreaker transitions to open.

3. The Application

1. Spring Boot application entry point

SpringCloudCircuitBreakerApplication.java

package com.taogen.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class SpringCloudCircuitBreakerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudCircuitBreakerApplication.class, args);
}
}

2. RestTemplate config

RestTemplateConfig.java

package com.taogen.springcloud;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

3. Request Eureka client services with Spring Cloud Breaker

MyController.java

package com.taogen.springcloud;

import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.function.Function;
import java.util.function.Supplier;

@RestController
@RequestMapping("/")
public class MyController {

@Autowired
private DiscoveryClient discoveryClient;

@Autowired
private RestTemplate restTemplate;

@Autowired
private CircuitBreakerFactory circuitBreakerFactory;

@GetMapping("/callServiceWithCircuitBreaker")
public String callServiceWithCircuitBreaker() {
String serviceId = "my-eureka-client";
String serviceUrl = discoveryClient.getInstances(serviceId).get(0).getUri().toString();
Supplier<String> supplier = () -> restTemplate.getForObject(serviceUrl + "/errorForTesting", String.class);
Function<Throwable, String> fallbackFunction = throwable -> {
if (throwable instanceof CallNotPermittedException) {
return "Circuit breaker is open, request cannot be processed.";
} else {
System.out.println("Error making request to service. " + throwable.getMessage());
// Fallback: Sets alternative paths for failing requests
return "This is a fallback message. Caused by " + throwable.getClass().getSimpleName() + ": " + throwable.getMessage();
}
};
// It must be the same as the circuit breaker ID defined in the application.yml resilience4j.circuitbreaker.instances.{instanceId}
String resilience4jCircuitBreakerId = "my-circuit-breaker-resilience4j";
// Circuit breaker: Stops making requests when an invoked service is failing
CircuitBreaker circuitBreaker = circuitBreakerFactory.create(resilience4jCircuitBreakerId);
String response = circuitBreaker.run(supplier, fallbackFunction);
System.out.println(response);
return response;
}
}

4. Start and Verification

1. Starting a Eureka server and a Eureka client. And write a /errorForTesting API in the Eureka client. For using Eureka, you can refer to A Guide to Spring Cloud Netflix Eureka.

The APIs in the Eureka Client

@Value("${spring.application.name}")
private String applicationName;

@GetMapping("/greeting")
public String greeting() {
return "Hello from " + applicationName + " at " + new Date();
}

@GetMapping("/errorForTesting")
public String errorForTesting() {
// This endpoint is intentionally left to throw an error for testing purposes
throw new RuntimeException("This is a test error for circuit breaker demonstration.");
}

2. Running the main() method in the SpringCloudCircuitBreakerApplication to start this Spring Cloud Circuit Breaker application.

3. Request the /callServiceWithCircuitBreaker endpoint to see the result.

According to the preceding resilience4j configuration in application.yml, if the number of requests is greater than 6 and the failure rate is equal to or greater than 60%, the Circuit Breaker will be open.

References

[1] Spring Microservices in Action, Second Edition

[2] Spring Cloud Circuit Breaker Documentation

[3] Resilience4j CircuitBreaker