<resilience4j.version>2.1.0</resilience4j.version>
<vertx.version>4.4.4</vertx.version>
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.
Warning
|
Resilience4j 2.0, which we’ll use here, requires Java 17. |
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
A text editor or IDE
Java 17 or higher
Maven or Gradle
The code of this project contains Maven and Gradle build files that are functionally equivalent.
First, your pom.xml
file should declare version properties:
pom.xml
: version properties<resilience4j.version>2.1.0</resilience4j.version>
<vertx.version>4.4.4</vertx.version>
Next, 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>
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")
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.
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());
Tip
|
The default settings make a lot more sense for a real-world application than our demonstration configuration. |
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.
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());
});
}
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) or gradlew 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.
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
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.
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
.
Last published: 2023-12-15 01:01:17 +0000.