What is the Spring Cloud Circuit Breaker abstraction and what problem does it solve?
answer
- Provider-neutral SPI: factory + run(supplier, fallback)
- CLOSED -> OPEN -> HALF_OPEN
- Resilience4j is the plugged-in engine
- App code depends only on Spring interfaces
- starter-circuitbreaker-resilience4j
basics
~10 sIt is a common Spring API (CircuitBreakerFactory / CircuitBreaker) for wrapping risky calls with a circuit breaker. Your code calls the abstraction, and the actual implementation (like Resilience4j) is plugged in separately.
solid answer
~40 sSpring Cloud Circuit Breaker is a provider-neutral abstraction over circuit-breaker implementations. A circuit breaker protects a call to a remote/failing dependency: after repeated failures it 'opens' and short-circuits further calls (failing fast or running a fallback) instead of hammering a broken service. The abstraction gives you two Spring interfaces — CircuitBreakerFactory to create a named CircuitBreaker, and CircuitBreaker with run(supplier) / run(supplier, fallback) to execute the protected code. Your application code depends only on those Spring types; the concrete engine (Resilience4j is the standard one, via spring-cloud-starter-circuitbreaker-resilience4j) is chosen by which starter is on the classpath. That decouples business logic from any specific resilience library, so you can swap implementations without touching call sites.
code
java · 21 linesimport org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
@Service
class QuoteService {
private final CircuitBreakerFactory<?, ?> cbFactory; // Spring SPI, not Resilience4j
private final RestClient restClient;
QuoteService(CircuitBreakerFactory<?, ?> cbFactory, RestClient restClient) {
this.cbFactory = cbFactory;
this.restClient = restClient;
}
String quoteOfTheDay() {
return cbFactory.create("quotes").run(
() -> restClient.get().uri("/quote").retrieve().body(String.class),
throwable -> "Fallback: no quote available" // used on failure or open circuit
);
}
}go deeper
Know it wraps risky calls and swaps in Resilience4j under the hood; name factory + run(supplier, fallback).
Explain the three states and that the id keys per-call-site config and state.
Articulate the decoupling value and how the provider is selected by classpath/starter.
Frame it as an SPI/portability decision and know the reactive variant and bundled Resilience4j modules exist.
## The pattern A **circuit breaker** is a resilience pattern (from Michael Nygard's *Release It!*). When a downstream dependency (an HTTP service, a DB, a queue) starts failing or timing out, keep calling it and you waste threads, pile up latency, and can cascade the failure across your system. A circuit breaker watches the outcome of calls and moves through three states: - **CLOSED** — calls pass through normally; failures are counted. - **OPEN** — once a failure threshold is crossed, calls are *short-circuited*: the breaker rejects them immediately (throwing quickly or invoking a fallback) without touching the dependency. - **HALF_OPEN** — after a wait period, a few trial calls are allowed; if they succeed the breaker closes again, otherwise it re-opens. ## What Spring Cloud Circuit Breaker adds The **Spring Cloud Circuit Breaker** project does *not* implement the breaker itself. It defines a thin **SPI (Service Provider Interface)** — a set of Spring interfaces in `org.springframework.cloud.client.circuitbreaker`: - **`CircuitBreakerFactory`** — a factory bean. You call `factory.create("someId")` to get a `CircuitBreaker` keyed by a string id. The id lets each logical call site have its own configuration and its own independent state. - **`CircuitBreaker`** — the thing you execute code through. Its core methods are: - `<T> T run(Supplier<T> toRun)` - `<T> T run(Supplier<T> toRun, Function<Throwable, T> fallback)` Your app depends only on these two interfaces. The actual engine is supplied by a starter on the classpath. The reference implementation is **Resilience4j**, pulled in via `spring-cloud-starter-circuitbreaker-resilience4j`, which auto-configures a `Resilience4JCircuitBreakerFactory`. (A reactive variant, `ReactiveCircuitBreakerFactory` / `ReactiveCircuitBreaker`, exists for `Mono`/`Flux`.) ## Why decouple Without the abstraction you would import Resilience4j types (or Hystrix, Spring Retry, etc.) directly into your service classes. That couples business logic to a specific library's API and to its threading/config model. With the abstraction: - Call sites are written once against Spring interfaces. - Swapping the provider is a dependency/config change, not a code change. - Configuration is centralized in `Customizer` beans (see related questions), keeping tuning out of call sites. ## When to use Use it around any call to a resource that can be slow or fail independently of your app: REST/gRPC calls to other services, third-party APIs, sometimes DB or cache calls. It is most valuable in microservice/distributed systems where a single sick dependency can otherwise drag down callers. ## Gotchas / edge cases - **It is not automatic** — the abstraction is programmatic; wrapping code in `run(...)` (or using the separate Resilience4j annotation leaf) is your responsibility. - **The id matters** — breakers are per-id; reuse the same id where you want shared state, distinct ids where you want isolation. - **A fallback is optional** — with the single-arg `run`, a failure (or open circuit) propagates as an exception; with the two-arg form the `Function<Throwable,T>` is invoked instead. - **Timeouts/bulkheads** — Resilience4j bundles TimeLimiter, Bulkhead, Retry, RateLimiter with the circuit breaker; the Spring abstraction surfaces them through the same factory's configuration but the `run` API you code against stays the same.
- Which dependency plugs the actual implementation in, and what happens if none is present?spring-cloud-starter-circuitbreaker-resilience4j auto-configures a Resilience4JCircuitBreakerFactory. Without any provider starter, no CircuitBreakerFactory bean is auto-configured, so injecting it fails and you have nothing to run calls through.
- Does the abstraction start the breaker for you or do you have to wrap calls yourself?You wrap them yourself: the abstraction is programmatic — you must route the risky call through CircuitBreaker.run(...). It does not intercept arbitrary method calls (that is the separate annotation-based leaf).
saying these in an interview costs you the question
- Thinking Spring Cloud Circuit Breaker implements the breaker itself rather than delegating to Resilience4j
- Believing calls are protected automatically without wrapping them in run(...)
- Confusing it with a load balancer or retry mechanism