Destino Dev

Blog

Circuit Breaker com Resilience4j no Spring Boot: tutorial passo a passo

Por John Pires · 16 de março de 2026

Circuit Breaker com Resilience4j no Spring Boot: tutorial passo a passo

1) O que é e por que importa

O padrão circuit breaker é uma técnica madura de engenharia de software para controlar falhas em sistemas distribuídos. Em microservices, quando um serviço downstream demora ou falha repetidamente, manter requisições abertas só agrava a latência e consome recursos. Um circuit breaker detecta falhas repetidas e corta (abre) a comunicação por um tempo, permitindo que o downstream se recupere.

Resilience4j é uma biblioteca leve e modular, feita para Java 8+, que implementa padrões de resiliência como circuit breaker, retry, rate limiter e bulkhead. Integrada ao Spring Boot, ela fornece anotações, configuração programática e métricas.

Por que isso importa para quem começa: sem circuit breaker, um serviço problemático pode causar efeito dominó (cascade failure). Com ele você protege recursos e ganha previsibilidade.

Analogia simples: pense em um fusível elétrico. Se muita corrente passa (falhas / latência), o fusível desarma para proteger o circuito. O circuit breaker faz isso para chamadas remotas: detecta padrões ruins e interrompe as chamadas até que as condições melhorem.

Prática: nunca conecte um cliente HTTP diretamente a muitos recursos externos sem uma camada de proteção — circuit breakers são essa camada.

2) Conceitos fundamentais com analogias

2.1 Estados do circuit breaker

Um circuit breaker típico tem 3 estados:

  • Closed: tudo normal; requests passam e contador de falhas é observado.
  • Open: o circuito está aberto; requests falham rapidamente ou caem para um fallback.
  • Half-Open: teste — permite algumas requisições para verificar se o downstream voltou.

Analogia: um guarda em uma ponte que, ao ver excesso de peso (falhas), fecha a ponte (open). Depois de um tempo, abre um corredor de teste (half-open) para checar se está seguro voltar a abrir totalmente (closed).

2.2 Métricas e limiares

O breaker usa métricas: número de chamadas, taxa de falhas, janelas de tempo. Configure um limiar (por exemplo, >50% de falha em 30s) para disparar o open. Resilience4j usa SlidingWindow por padrão.

Escolha limiares com base em testes e na criticidade do serviço. Limiares muito agressivos causam flapping; limiares muito tolerantes não protegem seu sistema.

3) Passo a passo com código comentado

3.1 Dependências e configuração básica

Contexto: primeiro passo é adicionar as dependências do Resilience4j e Spring Boot ao seu projeto Maven. A seguir um pom.xml mínimo com as dependências necessárias.

<!-- pom.xml: dependências essenciais -->
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>1.7.1</version>
  </dependency>

  <dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-micrometer</artifactId>
  </dependency>
</dependencies>

O que faz: adiciona Spring Web e Resilience4j com integração Spring Boot. O after: você terá as anotações e beans necessários para usar circuit breaker via @CircuitBreaker e para expor métricas via Micrometer.

3.2 Configuração via application.yml

Contexto: configure nomes, janelas e limiares no application.yml para controlar comportamento do breaker.

# application.yml: configuração do resilience4j
resilience4j:
  circuitbreaker:
    instances:
      remoteServiceCB:
        registerHealthIndicator: true
        slidingWindowType: TIME_BASED
        slidingWindowSize: 10
        minimumNumberOfCalls: 5
        failureRateThreshold: 50
        waitDurationInOpenState: 10s
        permittedNumberOfCallsInHalfOpenState: 3

O que faz: define um instance chamado remoteServiceCB com janela baseada em tempo, limiar de 50% e espera de 10s. O after: Resilience4j passa a aplicar essas regras quando o nome do circuit breaker for usado.

3.3 Anotações e fallback

Contexto: exemplo de serviço que faz chamada HTTP para um serviço externo; usamos @CircuitBreaker para aplicar proteção e um método de fallback que entrega resposta padrão.

package com.exemplo.service;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class RemoteService {

    private final RestTemplate restTemplate = new RestTemplate();

    // Aplica o circuit breaker com o nome configurado em application.yml
    @CircuitBreaker(name = "remoteServiceCB", fallbackMethod = "fallbackGet")
    public String getRemoteData() {
        // Chamada que pode falhar: tempo limite, 5xx, etc.
        return restTemplate.getForObject("http://localhost:8081/api/remote", String.class);
    }

    // Método de fallback: recebe a exceção original como último parâmetro
    public String fallbackGet(Exception ex) {
        return "fallback-response"; // resposta simples e determinística
    }
}

O que faz: quando a chamada falha repetidamente, o circuit breaker abre e a biblioteca redireciona as chamadas para fallbackGet. O after: você evita chamadas bloqueantes para o serviço instável e entrega uma resposta previsível.

3.4 Erros comuns e debugging

Contexto: quem está começando costuma ver o circuit breaker sem efeito ou com comportamento inesperado. Aqui listamos armadilhas frequentes e como diagnosticá-las.

  • Não configurar o nome do circuit breaker corretamente: o nome passado em @CircuitBreaker precisa existir no application.yml ou haverá comportamento default.
  • Usar RestTemplate sem timeouts: se não houver timeout, a chamada pode pendurar e distorcer as métricas.
  • Não ajustar minimumNumberOfCalls: se for maior que o número de chamadas em uma janela, o breaker nunca calcula taxa de falha.
  • Esquecer de tratar exceções específicas: por padrão, Resilience4j considera exceções lançadas. Se o seu cliente retorna respostas 500 sem lançar exception, o breaker não contará como falha.
Dica: habilite logs e exponha métricas Micrometer para ver estados e contadores do breaker em runtime.

Depois: para debugging, inspecione beans CircuitBreakerRegistry e use endpoints /actuator/metrics (quando configurado) para ver as métricas.

4) Exemplo prático completo

Contexto: vamos construir um mini-projeto com Controller que consome RemoteService e retorna a resposta ao cliente. Inclui fallback, configuração e um pequeno test manual (curl).

4.1 Estrutura do projeto

Arquitetura mínima:

  1. Application (main)
  2. Controller (exposes /api/data)
  3. Service (chama serviço externo com @CircuitBreaker)
  4. application.yml com configuração do CB

Crie um projeto Spring Boot padrão com as dependências já mostradas. A seguir os arquivos Java essenciais.

4.2 Código: Application e Controller

Contexto: classe main e controller que expõe o endpoint local que depende do RemoteService.

package com.exemplo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

// Controller
package com.exemplo.web;

import com.exemplo.service.RemoteService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DataController {

    private final RemoteService remoteService;

    public DataController(RemoteService remoteService) {
        this.remoteService = remoteService;
    }

    @GetMapping("/api/data")
    public String getData() {
        return remoteService.getRemoteData();
    }
}

O que faz: o controller chama o serviço que possui circuit breaker. Quando o downstream falha, o controller recebe a resposta de fallback.

4.3 Código: RemoteService com RestTemplate configurado

Contexto: RestTemplate com timeout para evitar chamadas pendentes que estragam métricas.

package com.exemplo.service;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class RemoteService {

    private final RestTemplate restTemplate;

    public RemoteService() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(2000); // 2s connect timeout
        factory.setReadTimeout(3000);    // 3s read timeout
        this.restTemplate = new RestTemplate(factory);
    }

    @CircuitBreaker(name = "remoteServiceCB", fallbackMethod = "fallbackGet")
    public String getRemoteData() {
        return restTemplate.getForObject("http://localhost:8081/api/remote", String.class);
    }

    public String fallbackGet(Exception ex) {
        // Aqui podemos logar e retornar uma resposta simples
        System.err.println("Fallback acionado: " + ex.getClass().getSimpleName() + " -> " + ex.getMessage());
        return "{\"status\":\"fallback\",\"data\":null}";
    }
}

O que faz: solicita ao serviço remoto com timeouts configurados; em caso de falhas repetidas, Resilience4j chama o fallback. O after: o endpoint /api/data continuará respondendo, evitando timeouts longos para o cliente.

4.4 Testando manualmente e observando comportamento

Contexto: para testar, inicialmente execute um servidor remoto simples (pode ser outro Spring Boot na porta 8081) que retorna 500 quando solicitado. Em seguida, faça múltiplas requisições ao seu endpoint /api/data e observe o comportamento do circuit breaker.

Comandos curl para testar:

# Requisições que devem falhar e acionar o CB
for i in {1..10}; do curl -s http://localhost:8080/api/data; echo; sleep 1; done

# Depois de aberto, as respostas virão do fallback imediatamente
curl -s http://localhost:8080/api/data

O que acontece: nas primeiras falhas, o CB contabiliza as chamadas. Se o limiar for atingido, ele abre e passa a chamar o fallback sem tentar o downstream até que o tempo de espera expire. Em half-open ele testará algumas chamadas.

4.5 Observabilidade

Contexto: conecte métricas ao Prometheus/Grafana ou use /actuator/metrics para consultar metrics do Resilience4j. Exemplo de fluxo: habilitar micrometer e consultar metricas como resilience4j.circuitbreaker.state.

Dica prática: adicione logs no fallback e use CircuitBreakerRegistry para inspecionar estado via JMX ou endpoints de debug.

5) Próximos passos para aprofundar

5.1 Padrões complementares

Para construir resiliência real, combine circuit breaker com:

  • Retry: repetir com backoff controlado para falhas transitórias.
  • Rate limiting: proteger seu serviço de overload.
  • Bulkhead: isolar recursos para evitar falhas em cascata.

Resilience4j oferece todos esses módulos e eles interagem bem quando combinados de forma consciente.

5.2 Testes e chaos engineering

Inclua testes que simulam latência e falhas (wiremock, MockWebServer) para validar configuração do breaker. Considere práticas de chaos engineering em ambiente controlado para validar comportamento em produção.

Exemplo: escrever testes de integração que simulam respostas 500 e confirmam que o fallback foi acionado e que o CB trocou de estado.

5.3 Performance e virtual threads

Se usar Java 19+ com virtual threads, prefira clientes HTTP não bloqueantes (WebClient) ou ajuste ThreadPools. O circuit breaker atua sobre a lógica, mas recursos de threads e I/O impactam as métricas. Teste com carga real.

Conclusão prática

Implementar circuit breaker com Resilience4j no Spring Boot é direto: adicione dependências, configure instâncias no application.yml, anote métodos com @CircuitBreaker e implemente fallbacks determinísticos. O ponto-chave para iniciantes é: configure timeouts, defina limiares realistas e exponha métricas para monitoramento.

Para começar rápido: 1) adicione Resilience4j, 2) configure um instance com slidingWindow e waitDuration, 3) envolva chamadas remotas com @CircuitBreaker e um fallback simples, 4) monitore com /actuator/metrics. Isso reduz o risco de falhas em cascata e aumenta a previsibilidade do seu serviço.

Resumo técnico: circuit breaker não é substituto para um design robusto; é uma ferramenta para mitigar falhas. Combine com retry, bulkhead e observabilidade para sistemas distribuídos saudáveis.

Checklist final:

  • Timeouts HTTP configurados
  • Nome do breaker consistente entre anotações e config
  • Mínimo de chamadas para observar falhas
  • Fallback determinístico e simples
  • Métricas e logs habilitados

Implementação prática e instrumentada é o melhor caminho para aprender: crie um serviço de teste, force falhas e observe como o circuit breaker protege seu ecossistema.

Assuntos: Java, spring-boot, resilience4j, circuit-breaker, microservices

Leia também