Deployment & Fault Tolerance

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:

  1. the CircuitBreakerVerticle class

  2. the VertxCircuitBreaker adapter, which is included in the repository of this how-to

What you need

  • A text editor or IDE

  • Java 17 or higher

  • Maven or Gradle

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:

Maven pom.xml: version properties
<resilience4j.version>2.1.0</resilience4j.version>
<vertx.version>5.0.0.CR2</vertx.version>

Next, import the Resilience4j and Vert.x BOMs to manage dependency versions:

Maven 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:

Maven 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:

Maven 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:

Gradle build.gradle.kts: BOM imports
implementation(platform("io.github.resilience4j:resilience4j-bom:2.1.0"))
implementation(platform("io.vertx:vertx-stack-depchain:5.0.0.CR2"))

Then, add a dependency on the Resilience4j circuit breaker library:

Gradle build.gradle.kts: Resilience4j dependency
implementation("io.github.resilience4j:resilience4j-circuitbreaker")

Finally, add dependencies on the Vert.x Web and Vert.x Web Client libraries:

Gradle build.gradle.kts: Vert.x dependencies
implementation("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:

The CircuitBreakerVerticle class
public class CircuitBreakerVerticle extends VerticleBase {
}

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:

Create the circuit breaker
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:

Create the endpoint
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())
      .send()
      .expecting(HttpResponseExpectation.SC_SUCCESS);
  })
    .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 Futures. 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:

Start the server
return 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:

The start method of the verticle
@Override
public Future<?> 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())
        .send()
        .expecting(HttpResponseExpectation.SC_SUCCESS);
    })
      .onSuccess(response -> ctx.end("Got: " + response.body() + "\n"))
      .onFailure(error -> ctx.end("Failed with: " + error.toString() + "\n"));
  });

  return 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:

The main method
public 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.

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:

  1. creating an instance of the Resilience4j circuit breaker,

  2. using the circuit breaker to guard an action whose result is a Vert.x Future.