Guard Your Vert.x Asynchronous Calls with the Built‑In Circuit Breaker
Learn how to use Vert.x’s Circuit Breaker to protect async services, configure timeouts, fallbacks, and monitor state via the event bus. Includes a concrete example and clustering caveats.
17 Aug 2025, 03:32 UTC

The Problem: Unreliable Async Services
In a Vert.x application, most calls to external systems—HTTP APIs, databases, MQTT brokers—are expressed as Future or Promise objects. When such a service starts to fail, each failure propagates back to the event loop, causing increased latency and, eventually, cascading outages. A classic way to protect against this is the circuit breaker, a pattern that stops repeated attempts to a failing service and optionally returns a fallback value.
What the Circuit Breaker Gives You
Vert.x ships a non‑blocking circuit breaker (io.vertx.circuitbreaker.CircuitBreaker) that:
- Tracks success/failure counts and opens the breaker when a threshold is reached.
- Implements configurable timeouts, failure ratios, and retry limits.
- Allows a fallback future that runs when the breaker is open or half‑open.
- Exposes state changes via the event bus address
vertx.circuitbreaker.{name}for external monitoring. - Works uniformly across Vert.x core, Web, and MQTT components.
Configuring a Circuit Breaker
Below is a typical configuration that opens the breaker after a 50% failure ratio and a 200 ms timeout per request. The setFallbackOnFailure(true) flag tells Vert.x to run the fallback whenever the protected operation fails or the breaker is open.
import io.vertx.core.Vertx;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.circuitbreaker.CircuitBreaker;
import io.vertx.circuitbreaker.CircuitBreakerOptions;
Vertx vertx = Vertx.vertx();
CircuitBreaker breaker = CircuitBreaker.create("db", vertx,
new CircuitBreakerOptions()
.setFailureRatio(0.5) // 50% failures open the breaker
.setTimeout(200) // 200 ms timeout per attempt
.setMaxFailures(5) // optional: absolute failure cap
.setFallbackOnFailure(true));
A Worked Example
The following verticle demonstrates a failing operation that will trigger the breaker. A fallback future returns a default value without contacting the failing service.
public class CircuitBreakerDemo extends AbstractVerticle {
@Override
public void start() {
CircuitBreaker breaker = CircuitBreaker.create("demo", vertx,
new CircuitBreakerOptions()
.setFailureRatio(0.5)
.setTimeout(200)
.setFallbackOnFailure(true));
// Event‑bus monitor
vertx.eventBus().consumer("vertx.circuitbreaker.demo", msg -> {
System.out.println("Breaker state: " + msg.body());
});
// Simulate a 5‑second interval of calls
vertx.setPeriodic(500, id -> {
breaker.executeWithFallback(
// The protected operation – deliberately fails
() -> Future.failedFuture("service down"),
// Fallback – returns a default value
() -> Future.succeededFuture("default value")
).onComplete(ar -> {
if (ar.succeeded()) {
System.out.println("Result: " + ar.result());
} else {
System.out.println("Unexpected failure: " + ar.cause());
}
});
});
}
}
When you run this verticle, the breaker will open after the configured failure ratio is exceeded. Subsequent calls will bypass the protected future and immediately return the fallback value.
Monitoring State on the Event Bus
Vert.x publishes a JSON message each time the breaker changes state. The message body contains the new state ("closed", "open", "half-open") and a timestamp. By listening to vertx.circuitbreaker.demo, you can log the transitions or feed them into an external monitoring system.
Limitations & Trade‑offs
- Mis‑configuration risk: A too‑low failure ratio or short timeout can cause the breaker to open prematurely, increasing fallback latency.
- Clustering: The breaker’s state lives in memory per verticle instance. In a clustered deployment, each node will have an independent breaker unless you use a shared store (e.g., Hazelcast). Without clustering, a failure on one node won’t affect others.
- Blocking fallbacks: The fallback future must be non‑blocking. If you block the event loop inside the fallback, you negate the benefit of the breaker.
What to Do Next
1. Add the circuit breaker to any async call that can fail repeatedly.
2. Tune failureRatio, timeout, and maxFailures based on your service’s error characteristics.
3. Enable a cluster manager (e.g., HazelcastClusterManager) if you need shared breaker state across nodes.
4. Hook into the event‑bus notifications to surface breaker metrics in your observability stack.
5. Write unit tests that simulate failures and verify the breaker opens and the fallback runs.
With these steps, your Vert.x application will gracefully handle transient and persistent failures without compromising the event‑loop’s responsiveness.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.