A step-by-step guide to implementing Reactive Resilience4j
Table of Contents· Why Reactive?
· Blocking vs Non Blocking
· Why Resilience4j?
· Step 1. Add POM Dependency
· Step 2. Add Circuit Breaker Configuration Bean
· Step 3. Add Configuration for Circuit Breaker Properties
· Step 4. Implementing Circuit Breaker
· Swagger UI not Working for Webflux
· How to Implement Circuit Breaker for Endpoints Which Don’t Have Return Type
This article will focus on implementing reactive Circuit Breaker using Resilience4j, a Spring Cloud Circuit Breaker library.
Let’s take a step back and take a closer look at some of the options available for REST API consumption.
The “blocking” side of the options in the diagram above are basically all based on the thread-per-request model. This means that the thread will block until the REST API client receives the response. If we have a lot of incoming requests, the application will create many threads, which will exhaust the thread pool or occupy all the available memory. Users may experience performance degradation.
Spring 5 Reactive framework introduced
WebClient, an asynchronous, non-blocking solution. The Reactive framework uses an event-driven architecture. It provides means to compose asynchronous logic through the Reactive Streams API. Compared to the synchronous/blocking method, the reactive approach can process more logic while using fewer threads and system resources. Moreover, using
WebClient We can make synchronous or asynchronous HTTP requests with a functional fluent API that can integrate directly into our existing Spring configuration and the WebFlux reactive framework.
For REST API consumption, the winner is clear! The non blocking reactive
WebClient it is!
There are two main libraries that we can use to implement Circuit Breaker. Netflix Hystrix, which embraces an Object-Oriented design where calls to external systems have to be wrapped in a
HystrixCommand offering multiple functionalities. However, In SpringOne 2019, Spring announced that Hystrix Dashboard will be removed from Spring Cloud 3.1 version which makes it officially deprecated. Not a good idea to use a deprecated library. So the choice is clear, Resilience4j it is!
Resilience4j is a standalone library inspired by Hystrix but build on the principles of Functional Programming. Resilience4J provides higher-order functions (decorators) to enhance any functional interface, lambda expression or method reference with a Circuit Breaker, Rate Limiter or Bulkhead.
Other advantages of Resilience4J include more fine tuned configuration options (eg the number successful executions needed to close the Circuit Breaker pattern) and a lighter dependencies footprint.
We are going to use two Spring Boot microservices to demonstrate how to implement reactive Circuit Breaker:
- customer-service, which acts as the REST API provider, offering customer CRUD endpoints.
- customer-service-client, which utilizes
WebClientthrough Spring Boot Starter Webflux library to call the REST APIs.
Now let’s dive into the detailed steps to implement Resilience4j for reactive Circuit Breaker.
Since we have chosen
WebClient to consume REST API, we need to add the Spring Cloud Circuit Breaker Reactor Resilience4J dependency to our REST client application.
CircuitBreakerConfig class comes with a set of default values for Circuit Breaker configuration, if we opt to use the default configuration values for all of our Circuit Breakers, we can create a
Customize bean that is passed a
ReactiveResilience4JCircuitBreakerFactory. The factory’s
configureDefault method can be used to provide a default configuration. Sample snippet as follows:
If we opt to use customized configuration values, we will need to define our bean as follows (“customer-service” is simply a sample REST client instance, you can use whatever instance name you give to your REST client app):
If we define our customized configuration bean, we will also need to add Circuit Breaker configuration in
application.ymlfor example (sample values only, numbers should be tweaked based on application usage scenarios):
failureRateThreshold: When the failure rate is equal or greater than the threshold the Circuit Breaker transitions to open and starts short-circuiting calls. In our case, this value is 50%, which means if 1 out of 2 requests are failed, the threshold will be reached, which will move the Circuit Breaker into an OPEN state.
minimumNumberOfCalls: This attribute ensures the failure rate is calculated once a minimum number of calls are executed. In our case, 10 requests must be executed before the failure rate calculation starts.
slidingWindowType: Configures the type of the sliding window which is used to record the outcome of calls when the Circuit Breaker is closed.
Sliding window can either be count-based or time-based.
slidingWindowSize: Configures the size of the sliding window which is used to record the outcome of calls when the Circuit Breaker is closed.
waitDurationInOpenState: The time that the Circuit Breaker should wait before transitioning from open to half-open. In our case, it’s 50 seconds.
permittedNumberOfCallsInHalfOpenState: Configures the number of permitted calls when the Circuit Breaker is half open. In our case, the limit is 3, which means only 3 requests will be processed in a 10-second window.
Now that all the configuration is in place, we can start decorating our REST API calls from client side using Circuit Breaker. In the sample below, we are injecting
CustomerCientController through constructor injection. We then use
webClient to trigger CRUD calls on the CustomerVO and/or customerId passed in. Notice the “
transform” sections where we are creating the
ReactiveCircuitBreaker instance for “customer-service” (
rcb of type
ReactiveCircuitBreaker) with the help of
ReactiveCircuitBreakerFactory. The line to execute circuit breaker is
rcb.run(...). In the sample controller below, when exceptions are thrown, we are returning a blank
CustomerVO object for POST/GET/PUT calls as fallback response. For DELETE call, we are returning the
customerId passed in as fallback. So instead of getting 500 Internal Server Error in case of REST API provider is down, with Circuit Breaker properly implemented, we are receiving fallback responses.
Since we introduced Webflux library to use
WebClient, you may notice your swagger UI doesn’t work initially. In order to make it work, ensure the following steps are implemented:
- Add the following dependency in pom:
For endpoints which return no content in its response body, such as the following endpoint in the REST API provider, on the REST client side, if we mark the corresponding method which calls this endpoint to return
ReactiveCircuitBreaker won’t work. You will see 500 server error in case the REST API provider is down, which completely defeats the purpose of having Circuit Breaker.
In non reactive Circuit Breaker implementation, for methods which don’t have return type, we can use “CheckedRunnable“,” do the following (example):
BUT, in reactive Circuit Breaker,
ReactiveCircuitBreaker doesn’t have such interface to decorate
CheckedRunnable, so what do we do? After some investigation and experimentation, I have noticed we can manipulate the return type for such endpoints to return a general type such as
String. Put it simply, if an endpoint such as a DELETE call returns
Void on the server side, we can still manipulate the return type of that DELETE call from client side to return a simple type as
String, just passing back the input String which was passed into that endpoint. For example, on the client side we can implement the DELETE call like this:
Notice we are returning
Mono<String> instead of
Mono<Void>and we are specifying the return type
String on line
.bodyToMono(String.class)which was the reason why we can simply call
run method to invoke the decorated reactive Circuit Breaker function. That’s the only way I can figure out to work around
ReactiveCircuitBreaker not having its
decorateCheckedRunnable method to handle methods which don’t have return types.
The source code for the demo apps mentioned in this article can be found in my GitHub repository.
Happy Coding! Happy Crafting!