skip to content

What is the Spring Cloud Circuit Breaker abstraction and what problem does it solve?

level: juniorimportance: must knowfreq 55%

answer

  1. Provider-neutral SPI: factory + run(supplier, fallback)
  2. CLOSED -> OPEN -> HALF_OPEN
  3. Resilience4j is the plugged-in engine
  4. App code depends only on Spring interfaces
  5. starter-circuitbreaker-resilience4j

basics

~10 s

It 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 s

Spring 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 lines
java
import 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

for a junior

Know it wraps risky calls and swaps in Resilience4j under the hood; name factory + run(supplier, fallback).

for a middle

Explain the three states and that the id keys per-call-site config and state.

for a senior

Articulate the decoupling value and how the provider is selected by classpath/starter.

for a principal

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

context