0Pricing
Spring Boot 4 Complete Guide · Aula

Disjuntores e Isolamento por Compartimentos

Proteja chamadas a serviços dependentes com disjuntores, compartimentos e métodos de fallback do Resilience4j.

Disjuntores e Isolamento por Compartimentos é uma aula grátis de Spring Boot 4 Complete Guide no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Spring Boot 4 Complete Guide, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Spring Boot 4 Complete Guide inclui 4 aulas no total.

Partes desta aula ainda não foram traduzidas e aparecem em inglês.

Why Resilience Matters

In a distributed system, a single slow or failing downstream service can cascade into a total outage. If your service keeps calling a dead dependency, threads pile up waiting on timeouts until the whole application becomes unresponsive.

Resilience engineering is about containing failure. Two foundational patterns:

  • Circuit Breaker — stop calling a failing dependency for a while, fail fast instead of waiting.
  • Bulkhead — isolate resources so one saturated dependency cannot exhaust the threads or connections needed by the rest.

In Spring Boot 4 we implement these with Resilience4j, a lightweight, functional fault-tolerance library that replaced the now end-of-life Hystrix.

Adding Resilience4j

Spring Boot 4 integrates Resilience4j through the resilience4j-spring-boot3 starter (compatible with Boot 3.x/4.x) plus Spring AOP. The annotations are activated by an aspect that wraps your method calls.

You also pull in spring-boot-starter-aop and, for metrics, spring-boot-starter-actuator with Micrometer.

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot3</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

How a Circuit Breaker Works

A circuit breaker is a state machine that monitors the failure rate of calls:

  • CLOSED — calls flow through normally. Failures are recorded in a sliding window.
  • OPEN — once the failure rate crosses a threshold, the breaker trips. Further calls fail immediately (no downstream call) for a wait duration.
  • HALF_OPEN — after the wait, a limited number of trial calls are permitted. If they succeed, it returns to CLOSED; if they fail, it returns to OPEN.

The key benefit: when a dependency is down, you fail fast instead of blocking threads on doomed calls, and you give the dependency room to recover.

Annotating a Protected Call

The @CircuitBreaker annotation wraps a method. The name ties it to a configuration instance, and fallbackMethod names a method to invoke when the call fails or the breaker is open.

The fallback must have the same signature plus a trailing Throwable (or a more specific exception) parameter.

@Service
public class PaymentClient {

    private final RestClient restClient;

    public PaymentClient(RestClient restClient) {
        this.restClient = restClient;
    }

    @CircuitBreaker(name = "paymentService", fallbackMethod = "fallbackCharge")
    public ChargeResult charge(ChargeRequest request) {
        return restClient.post()
                .uri("/charges")
                .body(request)
                .retrieve()
                .body(ChargeResult.class);
    }

    private ChargeResult fallbackCharge(ChargeRequest request, Throwable t) {
        return ChargeResult.deferred(request.id(), "payment temporarily unavailable");
    }
}

Configuring the Breaker

Circuit breaker behavior is tuned in application.yml. You define default settings and per-instances overrides keyed by the name you used in the annotation.

  • sliding-window-type — COUNT_BASED or TIME_BASED.
  • failure-rate-threshold — percent of failures to open the breaker.
  • wait-duration-in-open-state — how long to stay OPEN before HALF_OPEN.
  • permitted-number-of-calls-in-half-open-state — trial calls allowed.
  • slow-call-duration-threshold / slow-call-rate-threshold — treat slow calls as failures.
resilience4j:
  circuitbreaker:
    configs:
      default:
        sliding-window-type: COUNT_BASED
        sliding-window-size: 20
        failure-rate-threshold: 50
        slow-call-duration-threshold: 2s
        slow-call-rate-threshold: 80
        wait-duration-in-open-state: 10s
        permitted-number-of-calls-in-half-open-state: 5
        automatic-transition-from-open-to-half-open-enabled: true
    instances:
      paymentService:
        base-config: default
        failure-rate-threshold: 40

Modeling the State Machine in Plain Java

To internalize the logic, here is a stripped-down, framework-free model of the CLOSED to OPEN transition using a count-based window. This is conceptually what Resilience4j does for you behind the annotation.

Run it to see the breaker trip once the failure rate crosses the threshold.

public class MiniBreaker {
    enum State { CLOSED, OPEN }
    static State state = State.CLOSED;
    static int window = 10, threshold = 50;
    static boolean[] results = new boolean[window];
    static int idx = 0, count = 0;

    static void record(boolean failure) {
        results[idx] = failure;
        idx = (idx + 1) % window;
        if (count < window) count++;
        int fails = 0;
        for (int i = 0; i < count; i++) if (results[i]) fails++;
        int rate = count == 0 ? 0 : (fails * 100 / count);
        if (count == window && rate >= threshold) state = State.OPEN;
    }

    public static void main(String[] args) {
        boolean[] calls = {false,true,false,true,true,false,true,true,false,true};
        for (boolean fail : calls) {
            record(fail);
            System.out.println((fail ? "FAIL" : "OK  ") + " -> state=" + state);
        }
    }
}

Counting Records vs Ignoring Exceptions

Not every exception should trip the breaker. A 400 Bad Request means your input was wrong, not that the dependency is unhealthy — counting it as a failure would open the breaker for valid traffic.

Use record-exceptions to list throwables that count as failures, and ignore-exceptions for those that should pass through without affecting breaker state.

resilience4j:
  circuitbreaker:
    instances:
      paymentService:
        base-config: default
        record-exceptions:
          - java.io.IOException
          - java.util.concurrent.TimeoutException
          - org.springframework.web.client.HttpServerErrorException
        ignore-exceptions:
          - com.example.payments.InvalidCardException
          - org.springframework.web.client.HttpClientErrorException$BadRequest

Bulkhead Isolation

The bulkhead pattern (named after a ship's watertight compartments) limits how many concurrent calls a dependency can consume, so one slow service cannot drain the entire thread pool.

Resilience4j offers two flavors:

  • SemaphoreBulkhead — caps concurrent calls on the caller's thread. Lightweight, no extra threads.
  • ThreadPoolBulkhead — runs calls on a dedicated, bounded thread pool with a queue. Provides true isolation and only works with asynchronous returns (CompletableFuture).

If the bulkhead is full, the call is rejected with BulkheadFullException, which your fallback can handle.

Applying a Bulkhead

The @Bulkhead annotation limits concurrency. With type = THREADPOOL the method must return a CompletableFuture and runs on the bulkhead's own pool. You can stack it with @CircuitBreaker — the annotations compose.

@Service
public class InventoryClient {

    private final RestClient restClient;

    public InventoryClient(RestClient restClient) {
        this.restClient = restClient;
    }

    @CircuitBreaker(name = "inventory", fallbackMethod = "fallbackStock")
    @Bulkhead(name = "inventory", type = Bulkhead.Type.THREADPOOL)
    public CompletableFuture<StockLevel> getStock(String sku) {
        return CompletableFuture.completedFuture(
            restClient.get().uri("/stock/{sku}", sku)
                      .retrieve().body(StockLevel.class));
    }

    private CompletableFuture<StockLevel> fallbackStock(String sku, Throwable t) {
        return CompletableFuture.completedFuture(StockLevel.unknown(sku));
    }
}

Configuring Bulkheads

Semaphore and thread-pool bulkheads have separate config sections.

  • bulkhead (semaphore): max-concurrent-calls and max-wait-duration (how long a call waits for a permit before being rejected).
  • thread-pool-bulkhead: max-thread-pool-size, core-thread-pool-size, and queue-capacity.

Right-size these to the dependency's real capacity: a bulkhead larger than the downstream can handle defeats the purpose.

resilience4j:
  bulkhead:
    instances:
      paymentService:
        max-concurrent-calls: 25
        max-wait-duration: 50ms
  thread-pool-bulkhead:
    instances:
      inventory:
        core-thread-pool-size: 8
        max-thread-pool-size: 16
        queue-capacity: 20

Ordering, Fallbacks, and Observability

When multiple Resilience4j annotations decorate one method, they apply in a fixed aspect order (highest precedence first): Bulkhead → TimeLimiter → RateLimiter → CircuitBreaker → Retry. So Retry wraps the circuit breaker — a retried call that still fails counts toward the breaker.

Fallback design rules:

  • Keep fallbacks fast and side-effect-free; never call the failing dependency again.
  • Return degraded-but-valid data (cached value, default, queued-for-later).
  • Inspect the Throwable to distinguish CallNotPermittedException (breaker open) from BulkheadFullException (overloaded).

Actuator exposes state and metrics at /actuator/circuitbreakers and via Micrometer (resilience4j_circuitbreaker_state), so you can alert on OPEN breakers.

private ChargeResult fallbackCharge(ChargeRequest request, CallNotPermittedException ex) {
    return ChargeResult.deferred(request.id(), "breaker open");
}

private ChargeResult fallbackCharge(ChargeRequest request, BulkheadFullException ex) {
    return ChargeResult.rejected(request.id(), "system busy");
}

private ChargeResult fallbackCharge(ChargeRequest request, Throwable t) {
    return ChargeResult.deferred(request.id(), "payment unavailable");
}

Quick Check

Test your understanding of bulkhead isolation.

Recap

You learned how to protect downstream calls in Spring Boot 4 with Resilience4j:

  • Circuit breakers fail fast via the CLOSED → OPEN → HALF_OPEN state machine, tuned with sliding-window size, failure-rate and slow-call thresholds, and wait duration.
  • Use record-exceptions / ignore-exceptions so client errors (4xx) don't trip the breaker.
  • Bulkheads cap concurrency — SemaphoreBulkhead on the caller thread, ThreadPoolBulkhead for true isolation with CompletableFuture.
  • Fallback methods share the signature plus a trailing Throwable; return degraded-but-valid data and never re-call the failing dependency.
  • Annotation order is Bulkhead → TimeLimiter → RateLimiter → CircuitBreaker → Retry; observe state through Actuator and Micrometer.

Together these patterns keep one failing dependency from taking down your whole service.

Perguntas Frequentes

A aula “Disjuntores e Isolamento por Compartimentos” é grátis?

Sim — o texto completo de “Disjuntores e Isolamento por Compartimentos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Spring Boot 4 Complete Guide, atualize para CoddyKit PRO. O curso de Spring Boot 4 Complete Guide inclui 4 aulas no total.

O que vou aprender em “Disjuntores e Isolamento por Compartimentos”?

Proteja chamadas a serviços dependentes com disjuntores, compartimentos e métodos de fallback do Resilience4j. Você pratica Spring Boot 4 Complete Guide com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Spring Boot 4 Complete Guide?

Nenhuma experiência prévia é necessária. Spring Boot 4 Complete Guide no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “Disjuntores e Isolamento por Compartimentos”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Spring Boot 4 Complete Guide?

Sim. Cada aula de Spring Boot 4 Complete Guide inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Propagação de Contexto e Instrumentação de Intervalos
  2. Disjuntores e Isolamento por Compartimentos
  3. Limitação de Taxa, Novas Tentativas e Limitadores de Tempo
  4. Correlação de Registros, Métricas e Rastreamentos
← Voltar para Spring Boot 4 Complete Guide