Resilience: Circuit Breakers
Stop one failing service from cascading: circuit breakers, retries, timeouts, and fallbacks with Resilience4j.
On this page
In a monolith, a slow method is a slow method. In a distributed system, a slow service is contagious: if lending calls catalog and catalog hangs, lending's threads pile up waiting, and soon lending falls over too - then whatever calls lending. One failure cascades into an outage. Resilience4j gives you the patterns to stop the spread.
The circuit breaker
The headline pattern is the circuit breaker, named after the electrical kind. It watches calls to a downstream service; when failures cross a threshold, it opens - and for a while, calls fail fast (with a fallback) instead of waiting on the broken service:
@CircuitBreaker(name = "catalog", fallbackMethod = "cachedBook")
public Book fetchBook(String isbn) {
return catalogClient.getBook(isbn); // may fail if catalog is down
}
private Book cachedBook(String isbn, Throwable cause) {
return bookCache.get(isbn); // graceful fallback
}The breaker has three states: closed (calls flow normally), open (calls fail fast after too many failures), and half-open (it periodically lets a trial call through to see if the service has recovered, then closes if it has).
When a wire shorts, the breaker in your fuse box trips and cuts power to that circuit - protecting the rest of the house from the fault and preventing a fire. It doesn't stay off forever; you reset it once the problem is fixed. A software circuit breaker does exactly this: it trips when a service keeps failing, protects the callers from piling up on it, and later tries again to see if it's healthy.
The rest of the toolkit
Resilience4j bundles complementary patterns:
- Retry — try a failed call a few times (for transient blips), with backoff.
- Timeout — never wait forever; bound how long a call may take.
- Rate limiter & bulkhead — cap concurrency so one dependency can't consume all your threads.
Used together: a timeout stops one call hanging, a retry rides out a blip, and a circuit breaker gives up when the service is truly down - returning a fallback so the user still gets a degraded-but-working response.
Always have a fallback plan
Fail fast is only half of resilience; the other half is degrading gracefully. A fallback - a cached value, a default, a clear "temporarily unavailable" - keeps your service usable when a dependency is down, instead of failing the whole request.
Lending calls catalog on every request. Catalog goes down and starts timing out. Without any resilience, describe how this takes lending down too - then explain how a circuit breaker with a fallback keeps lending working (in a degraded way).
What does a circuit breaker do when a downstream service keeps failing?
Key takeaways
- In distributed systems, a failing service cascades: callers block waiting on it and fall over too.
- A circuit breaker (Resilience4j) opens when failures cross a threshold, failing fast with a fallback so calls don't pile up.
- Its states are closed (normal), open (fail fast), and half-open (trial calls to test recovery).
- Combine with retry (ride out blips), timeout (bound waiting), and bulkheads (cap concurrency).
- Resilience means degrading gracefully with a fallback, not just failing - keep the service usable when a dependency is down.