Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
1. Overview
In this tutorial, weβll introduce the Spring Cloud Circuit Breaker project and learn how we can make use of it.
First, weβre going to see what the Spring Cloud Circuit Breaker offers in addition to existing circuit breaker implementations. Next, weβll learn how to use the Spring Boot auto-configuration mechanism to integrate one or more circuit breakers into our application.
Note that weβve got more information about what a circuit breaker is and how they work in Introduction to Hystrix, Spring Cloud Netflix Hystrix, and Guide to Resilience4j.
2. Spring Cloud Circuit Breaker
Until recently, Spring Cloud only provided us one way to add circuit breakers in our applications. This was through the use of Netflix Hystrix as part of the Spring Cloud Netflix project.
The Spring Cloud Netflix project is really just an annotation-based wrapper library around Hystrix. Therefore, these two libraries are tightly-coupled. This means we canβt switch to another circuit breaker implementation without changing the application.
The Spring Cloud Circuit Breaker project solves this. It provides an abstraction layer across different circuit breaker implementations. Itβs a pluggable architecture. So, we can code against the provided abstraction/interface and switch to another implementation based on our needs.
For our examples, weβll focus only on the Resilience4J implementation. However, these techniques can be used for other plugins.
3. Auto Configuration
In order to use a specific circuit breaker implementations in our application, we need to add the appropriate Spring starter. In our case, letβs use spring-cloud-starter-circuitbreaker-resilience4j:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
<version>1.0.2.RELEASE</version>
</dependency>
The auto-configuration mechanism configures the necessary circuit breaker beans if it sees one of the starters in the classpath.
If we wanted to disable the Resilience4J auto-configuration, we could set the spring.cloud.circuitbreaker.resilience4j.enabled property to false.
4. A Simple Circuit Breaker Example
Letβs create a web application using Spring Boot to allow us to explore how the Spring Cloud Circuit Breaker library works.
Weβll build a simple web service returning a list of albums. Letβs suppose the raw list is provided by a third-party service. For simplicity, weβll use an external dummy API provided by Jsonplaceholder to retrieve the list:
https://jsonplaceholder.typicode.com/albums
4.1. Create a Circuit Breaker
Letβs create our first circuit breaker. Weβll start by injecting an instance of the CircuitBreakerFactory bean:
@Service
public class AlbumService {
@Autowired
private CircuitBreakerFactory circuitBreakerFactory;
//...
}
Now, we can easily create a circuit breaker using the CircuitBreakerFactory#create method. It takes the circuit breaker identifier as an argument:
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitbreaker");
4.2. Wrap a Task in a Circuit Breaker
In order to wrap and run a task protected by the circuit breaker, we need to call the run method which takes a Supplier as an argument.
public String getAlbumList() {
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitbreaker");
String url = "https://jsonplaceholder.typicode.com/albums";
return circuitBreaker.run(() -> restTemplate.getForObject(url, String.class));
}
The circuit breaker runs our method for us and provides fault tolerance.
Sometimes, our external service could take too long to respond, throw an unexpected exception or the external service or host does not exist. In that case, we can provide a fallback as a second argument to the run method:
public String getAlbumList() {
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitbreaker");
String url = "http://localhost:1234/not-real";
return circuitBreaker.run(() -> restTemplate.getForObject(url, String.class),
throwable -> getDefaultAlbumList());
}
The lambda for the fallback receives the Throwable as an input, describing the error. This means we can provide different fallback results to the caller, based on the type of exception that triggered the fallback.
In this case, we wonβt take the exception into account. Weβll just return a cached list of albums.
If the external call ends with an exception and no fallback is provided, a NoFallbackAvailableException is thrown by Spring.
4.3. Build a Controller
Now, letβs finish our example and create a simple controller that calls the service methods and presents the results through a browser:
@RestController
public class Controller {
@Autowired
private Service service;
@GetMapping("/albums")
public String albums() {
return service.getAlbumList();
}
}
Finally, letβs call the REST service and see the results:
[GET] http://localhost:8080/albums
5. Global Custom Configuration
Usually, the default configuration is not enough. For this reason, we need to create circuit breakers with custom configurations based on our use cases.
In order to override the default configuration, we need to specify our own beans and properties in a @Configuration class.
Here, weβre going to define a global configuration for all circuit breakers. For this reason, we need to define a Customizer<CircuitBreakerFactory> bean. So letβs use the Resilience4JCircuitBreakerFactory implementation.
First, weβll define circuit breaker and time limiter configuration classes as per the Resilience4j tutorial:
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofMillis(1000))
.slidingWindowSize(2)
.build();
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
.timeoutDuration(Duration.ofSeconds(4))
.build();
Next, letβs embed the configuration in a Customizer bean by using the Resilience4JCircuitBreakerFactory.configureDefault method:
@Configuration
public class Resilience4JConfiguration {
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> globalCustomConfiguration() {
// the circuitBreakerConfig and timeLimiterConfig objects
return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.timeLimiterConfig(timeLimiterConfig)
.circuitBreakerConfig(circuitBreakerConfig)
.build());
}
}
6. Specific Custom Configuration
Of course, we can have multiple circuit breakers in our application. Therefore, in some cases, we need a specific configuration for every circuit breaker.
Similarly, we can define one or more Customizer beans. Then, we can provide a different configuration for each one by using the Resilience4JCircuitBreakerFactory.configure method:
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> specificCustomConfiguration1() {
// the circuitBreakerConfig and timeLimiterConfig objects
return factory -> factory.configure(builder -> builder.circuitBreakerConfig(circuitBreakerConfig)
.timeLimiterConfig(timeLimiterConfig).build(), "circuitBreaker");
}
Here we provide a second parameter, the id of the circuit breaker weβre configuring.
We can also set up multiple circuit breakers with the same configuration by providing a list of circuit breaker ids to the same method:
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> specificCustomConfiguration2() {
// the circuitBreakerConfig and timeLimiterConfig objects
return factory -> factory.configure(builder -> builder.circuitBreakerConfig(circuitBreakerConfig)
.timeLimiterConfig(timeLimiterConfig).build(),
"circuitBreaker1", "circuitBreaker2", "circuitBreaker3");
}
7. Alternative Implementations
Weβve seen how to use the Resilience4j implementation to create one or more circuit breakers with Spring Cloud Circuit Breaker.
However, there are other implementations supported by Spring Cloud Circuit Breaker that we can leverage in our application:
Itβs worth mentioning that we can mix and match different circuit breaker implementations in our application. Weβre not just limited to one library.
The above libraries have more capabilities than weβve explored here. However, Spring Cloud Circuit Breaker is an abstraction over only the circuit breaker part. For example, Resilience4j also provides other modules like RateLimiter, Bulkhead, Retry in addition to the CircuitBreaker and TimeLimiter modules used in this article.
8. Conclusion
In this article, we discovered the Spring Cloud Circuit Breaker project.
First, we learned what the Spring Cloud Circuit Breaker is, and how it allows us to add circuit breakers to our application.
Next, we leveraged the Spring Boot auto-configuration mechanism in order to show how to define and integrate circuit breakers. Also, we demonstrated how the Spring Cloud Circuit Breaker works through a simple REST service.
Finally, we learned to configure all circuit breakers together, as well as individually.
