Capítulo 18: Resiliência e Circuit Breaker com Resilience4j
Especialização em Backend com Java & Spring Boot • Spring Boot 3 & Java 21 LTS • Spring Data JPA, Security, Microsserviços e Cloud
🗺️ Mapa Conceitual do Tópico
flowchart TD
A["Cliente HTTP / Frontend"] --> B["API Gateway / Router"]
B --> C["Controller / Handler"]
C --> D["Service Layer (Regras de Negócio)"]
D --> E["Repository / ORM (Persistência)"]
E --> F["Banco de Dados / Cache"]
subgraph ARQ["Arquitetura do Capítulo"]
G["Conceito: Resiliência e Circuit Breaker com Resilience4j"]
H["Segurança, Validação e Resiliência"]
I["Alta Performance e Escalabilidade"]
end
D --> ARQ
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style D fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
style F fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
🏛️ 1. Fundamentos Técnicos de Resiliência e Circuit Breaker com Resilience4j
Por que falhas em cascata são o maior risco de microsserviços. Quando o serviço de pagamentos fica lento (não fora do ar, apenas lento), cada requisição que chega ao serviço de pedidos fica presa esperando resposta, consumindo uma thread do pool por chamada pendente. Se o pool se esgota, o serviço de pedidos para de atender qualquer requisição — inclusive as que nada têm a ver com pagamento. Um serviço lento derruba serviços saudáveis a jusante por exaustão de recursos: esse é o efeito cascata que os padrões de resiliência do Resilience4j existem para conter.
Circuit Breaker: os três estados. @CircuitBreaker(name = "pagamentoService", fallbackMethod = "fallbackPagamento") monitora a taxa de falha das chamadas ao método protegido. No estado CLOSED (normal), todas as chamadas passam e são contabilizadas; se a taxa de falha ultrapassar o limiar configurado (ex.: 50% em uma janela de N chamadas), o circuito abre para OPEN: toda chamada subsequente é rejeitada instantaneamente, sem sequer tentar a chamada real, acionando o método de fallback imediatamente — isso protege o serviço degradado de receber mais carga enquanto se recupera, e protege o chamador de ficar bloqueado esperando timeouts repetidos. Após um tempo de espera configurado, o circuito passa a HALF_OPEN: libera um número limitado de chamadas de teste; se tiverem sucesso, volta a CLOSED; se falharem, volta a OPEN.
O método de fallback: contrato de assinatura. O método indicado em fallbackMethod deve ter a mesma assinatura de retorno do método original, mais um parâmetro final Throwable (ou o tipo específico da exceção esperada) — o Resilience4j o invoca automaticamente quando o circuito está aberto ou a chamada original lança exceção, permitindo devolver uma resposta de contingência (cache, valor padrão, fila para reprocessamento posterior) em vez de propagar o erro ao cliente final.
Retry com backoff: diferença entre falha transitória e falha persistente. Nem toda falha justifica desistir imediatamente — uma instabilidade momentânea de rede pode se resolver na tentativa seguinte. @Retry(name = "consultaEstoque") reexecuta automaticamente o método até o número máximo de tentativas configurado, tipicamente com espera crescente entre elas (backoff exponencial) para não bombardear um serviço já sobrecarregado com tentativas imediatas. É crucial combinar Retry com Circuit Breaker corretamente: sem essa combinação, o Retry pode continuar tentando repetidamente um serviço que o Circuit Breaker já identificou como fora do ar, anulando a proteção.
RateLimiter e Bulkhead: dois tipos de isolamento distintos. @RateLimiter limita quantas chamadas por unidade de tempo são permitidas, protegendo um recurso downstream de ser sobrecarregado por excesso de tráfego (proteção do lado do produtor de carga). @Bulkhead (inspirado nos compartimentos estanques de um navio, que impedem que uma única avaria afunde o casco inteiro) limita quantas chamadas concorrentes simultâneas um recurso pode receber, isolando um pool de threads/semáforos dedicado — se um serviço de relatórios pesados trava, o Bulkhead garante que apenas as threads alocadas para ele fiquem presas, sem esgotar o pool compartilhado usado pelo resto da aplicação.
Observabilidade do estado do circuito. Um Circuit Breaker aberto silenciosamente é um sintoma de produção que precisa ser visível — o Actuator expõe /actuator/circuitbreakers e /actuator/circuitbreakerevents, reportando o estado atual (CLOSED/OPEN/HALF_OPEN) e o histórico de transições de cada instância registrada em CircuitBreakerRegistry, permitindo alertas automáticos quando um circuito abre — sinal de que uma dependência downstream está degradada e precisa de atenção operacional imediata.
💻 2. Código de Demonstração Corporativo
package com.empresa.api.service;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.retry.annotation.Retry;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
@Service
public class PagamentoGatewayService {
private final PagamentoGatewayClient gatewayClient;
public PagamentoGatewayService(PagamentoGatewayClient gatewayClient) {
this.gatewayClient = gatewayClient;
}
// Retry primeiro (tenta 2x em falhas transitórias); CircuitBreaker por fora corta o
// fluxo inteiro (retries incluídos) se a taxa de falha do gateway ultrapassar o limiar.
@CircuitBreaker(name = "pagamentoGateway", fallbackMethod = "fallbackProcessarPagamento")
@Retry(name = "pagamentoGateway")
public StatusPagamento processarPagamento(Long pedidoId, double valor) {
return gatewayClient.cobrar(pedidoId, valor);
}
private StatusPagamento fallbackProcessarPagamento(Long pedidoId, double valor, Throwable t) {
// Gateway indisponível: enfileira para reprocessamento assíncrono em vez de falhar a compra.
System.err.println("Gateway de pagamento indisponível para pedido " + pedidoId + ": " + t.getMessage());
return StatusPagamento.PENDENTE_REPROCESSAMENTO;
}
}
# application.yml
resilience4j:
circuitbreaker:
instances:
pagamentoGateway:
sliding-window-size: 10
failure-rate-threshold: 50 # abre o circuito com 50% de falha na janela
wait-duration-in-open-state: 15s # tempo em OPEN antes de testar HALF_OPEN
permitted-number-of-calls-in-half-open-state: 3
retry:
instances:
pagamentoGateway:
max-attempts: 3
wait-duration: 500ms
enable-exponential-backoff: true
exponential-backoff-multiplier: 2
bulkhead:
instances:
relatoriosPesados:
max-concurrent-calls: 5
🔗 Recursos Pedagógicos do Capítulo 18
| Recurso Didático | Finalidade | Link de Acesso |
|---|---|---|
| 📊 Slides de Aula | Apresentação visual interativa com Dark Mode e suporte a teclado | Ver Slides |
| 🧠 Quiz Formativo | Teste interativo de fixação com feedback imediato por alternativa | Fazer Quiz |
| 💻 Exemplos de Código | Demonstrações funcionais com código executável | Ver Exemplos |
| 🧩 Exercícios em 4 Níveis | Lista progressiva de fixação com gabarito em bloco colapsável | Resolver Exercícios |
| ⬅️ Capítulo Anterior | 📚 Sumário de Tópicos | Próximo Capítulo ➡️ |