<resilience4j.version>2.1.0</resilience4j.version>
<vertx.version>4.4.4</vertx.version>
Using Resilience4j with Vert.x
This document will show you how to use Resilience4j in Vert.x applications.
Resilience4j is a popular library that implements common fault tolerance strategies:
-
bulkhead (concurrency limiter)
-
circuit breaker
-
rate limiter
-
retry
-
time limiter (timeout)
In this how-to, we only demonstrate the usage of a circuit breaker, but the repository of this how-to contains Vert.x adapters for all strategies mentioned above.
Resilience4j 2.0, which we’ll use here, requires Java 17. |
What you will build
You will use a circuit breaker with an HTTP client to prevent executing requests against a server that keeps returning errors. This serves two main purposes:
-
avoid generating more load on a server that may be overloaded,
-
failing fast when failure is very likely to occur anyway.
The application consists of a few classes:
-
the
CircuitBreakerVerticle
class -
the
VertxCircuitBreaker
adapter, which is included in the repository of this how-to
Create a project
The code of this project contains Maven and Gradle build files that are functionally equivalent.
Using Maven
First, your pom.xml
file should declare version properties:
pom.xml
: version propertiesNext, import the Resilience4j and Vert.x BOMs to manage dependency versions:
pom.xml
: BOM imports<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bom</artifactId>
<version>${resilience4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-stack-depchain</artifactId>
<version>${vertx.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Then, add a dependency on the Resilience4j circuit breaker library:
pom.xml
: Resilience4j dependency<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
</dependency>
Finally, add dependencies on the Vert.x Web and Vert.x Web Client libraries:
pom.xml
: Vert.x dependencies<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
</dependency>
Using Gradle
The dependencies
block in your build.gradle.kts
file (assuming you use Gradle with the Kotlin DSL) should first import the Resilience4j and Vert.x BOMs to manage dependency versions:
build.gradle.kts
: BOM importsimplementation(platform("io.github.resilience4j:resilience4j-bom:2.1.0"))
implementation(platform("io.vertx:vertx-stack-depchain:4.4.4"))
Then, add a dependency on the Resilience4j circuit breaker library:
build.gradle.kts
: Resilience4j dependencyimplementation("io.github.resilience4j:resilience4j-circuitbreaker")
Finally, add dependencies on the Vert.x Web and Vert.x Web Client libraries:
build.gradle.kts
: Vert.x dependenciesimplementation("io.vertx:vertx-web")
implementation("io.vertx:vertx-web-client")
Using circuit breaker to guard Vert.x Web Client requests
First, let’s create the main CircuitBreakerVerticle
class:
CircuitBreakerVerticle
classpublic class CircuitBreakerVerticle extends AbstractVerticle {
}
This class will use the VertxCircuitBreaker
class, which is provided in the repository of this how-to.
Creating the circuit breaker
In the start
method of the verticle, let’s create a single circuit breaker instance that will be shared among all requests. For demonstration purposes, we will configure the circuit breaker to start calculating the failure rate after mere 5 requests:
CircuitBreaker cb = CircuitBreaker.of("my-circuit-breaker", CircuitBreakerConfig.custom()
.minimumNumberOfCalls(5)
.build());
The default settings make a lot more sense for a real-world application than our demonstration configuration. |
Creating a server
Next, we’ll use Vert.x Web to expose a simple endpoint. The sole purpose of that endpoint will be to perform an HTTP request and guard it with the circuit breaker:
Router router = Router.router(vertx);
WebClient client = WebClient.create(vertx);
router.get("/").handler(ctx -> {
VertxCircuitBreaker.executeFuture(cb, () -> {
return client.get(8080, "localhost", "/does-not-exist")
.as(BodyCodec.string())
.expect(ResponsePredicate.SC_SUCCESS)
.send();
})
.onSuccess(response -> ctx.end("Got: " + response.body() + "\n"))
.onFailure(error -> ctx.end("Failed with: " + error.toString() + "\n"));
});
This deserves some explanation.
The endpoint on /
uses the VertxCircuitBreaker
adapter that glues together the Resilience4j API and Vert.x Future
s. The adapter expects a Supplier<Future>
as an action to be guarded.
The Future
here comes from the Vert.x Web Client get(8080, "localhost", "/does-not-exist")
call.
As you can imagine, the request we make here will never succeed. Therefore, the circuit breaker will kick in after 5 requests (as configured above) and will prevent further calls to the guarded action. Instead, the Future
returned by executeFuture
will fail instantly with the CallNotPermittedException
exception.
Starting the server
Finally, we’ll start the server:
vertx.createHttpServer()
.requestHandler(router)
.listen(8080)
.onSuccess(server -> {
System.out.println("HTTP server started on port " + server.actualPort());
});
The entire start
method of the verticle looks like this:
start
method of the verticle@Override
public void start() {
CircuitBreaker cb = CircuitBreaker.of("my-circuit-breaker", CircuitBreakerConfig.custom()
.minimumNumberOfCalls(5)
.build());
Router router = Router.router(vertx);
WebClient client = WebClient.create(vertx);
router.get("/").handler(ctx -> {
VertxCircuitBreaker.executeFuture(cb, () -> {
return client.get(8080, "localhost", "/does-not-exist")
.as(BodyCodec.string())
.expect(ResponsePredicate.SC_SUCCESS)
.send();
})
.onSuccess(response -> ctx.end("Got: " + response.body() + "\n"))
.onFailure(error -> ctx.end("Failed with: " + error.toString() + "\n"));
});
vertx.createHttpServer()
.requestHandler(router)
.listen(8080)
.onSuccess(server -> {
System.out.println("HTTP server started on port " + server.actualPort());
});
}
Running the application
The CircuitBreakerVerticle
also needs a main
method:
main
methodpublic static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(new CircuitBreakerVerticle());
}
Then you can run the application:
-
straight from your IDE, or
-
with Maven:
mvn clean compile exec:java
, or -
with Gradle:
./gradlew run
(Linux, macOS) orgradlew run
(Windows).
The following examples use the HTTPie command line HTTP client. Please refer to the installation documentation if you don’t have it installed on your system yet.
First 5 requests
For the first 5 requests, the circuit breaker does not calculate the failure rate and will allow all actions to proceed. As expected, they will all fail:
http localhost:8080
http localhost:8080
http localhost:8080
http localhost:8080
http localhost:8080
You should see the following output 5 times:
Failed with: io.vertx.core.impl.NoStackTraceThrowable: Response status code 404 is not between 200 and 300
Next, the circuit breaker kicks in
http localhost:8080
The 6th request will fail with a different message:
Failed with: io.github.resilience4j.circuitbreaker.CallNotPermittedException: CircuitBreaker 'my-circuit-breaker' is OPEN and does not permit further calls
We see that the circuit breaker prevented the execution of the request against the server.
Summary
This document covered:
-
creating an instance of the Resilience4j circuit breaker,
-
using the circuit breaker to guard an action whose result is a Vert.x
Future
.