Introdução
Quem desenvolve microsserviços sabe: falhas acontecem. Conexões que caem, serviços externos lentos, filas congestionadas — tudo isso pode comprometer a disponibilidade de um sistema.
O Quarkus, além de rápido e otimizado para ambientes nativos em Kubernetes, já vem com suporte a recursos de resiliência por meio do MicroProfile Fault Tolerance. Isso significa que podemos aplicar, com poucas anotações, padrões clássicos de tolerância a falhas em nossas aplicações.
Neste artigo, vou mostrar como implementar esses padrões de forma prática, com exemplos de código e dicas que podem salvar sua aplicação no ambiente de produção.
Entendendo o MicroProfile Fault Tolerance
O MicroProfile Fault Tolerance é uma especificação pensada para microsserviços Java. Ele permite que você adicione mecanismos de proteção contra falhas sem precisar escrever toda a lógica manualmente.
Entre os recursos disponíveis estão:
- 🔄 Retry: repete a execução quando ocorre falha transitória.
- ⏱️ Timeout: define o tempo máximo de resposta.
- 🚦 Circuit Breaker: corta chamadas para um serviço instável, protegendo a aplicação.
- 🛠️ Fallback: oferece uma alternativa quando a lógica principal falha.
- 🧱 Bulkhead: controla o número de requisições concorrentes para evitar saturação.
Para usar no Quarkus, basta incluir a extensão:
./mvnw quarkus:add-extension -Dextensions="smallrye-fault-tolerance"
Exemplos de Uso na Prática
Retry – Tentativas Extras
Quando uma integração externa falha por instabilidade momentânea, repetir a chamada pode resolver.
@Retry(maxRetries = 3, delay = 500)
public String processarTransacao() {
if (Math.random() > 0.5) {
throw new RuntimeException("Erro temporário no provedor!");
}
return "Transação concluída.";
}
👉 Neste exemplo, a operação será reexecutada até 3 vezes, com intervalo de 500 ms entre as tentativas.
Timeout – Colocando Limite de Espera
Não é saudável deixar sua aplicação presa em chamadas que nunca terminam.
@Timeout(2000)
public String gerarRelatorio() {
try {
Thread.sleep(4000); // Simula lentidão
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Relatório gerado.";
}
👉 Se a operação passar de 2 segundos, ela será interrompida e a aplicação segue em frente.
Circuit Breaker – Evitando Cascata de Falhas
Um serviço instável pode causar sobrecarga se continuar sendo chamado repetidamente.
@CircuitBreaker(requestVolumeThreshold = 4, failureRatio = 0.5, delay = 5000)
public String consultarCatalogo() {
if (Math.random() > 0.7) {
throw new RuntimeException("Falha no catálogo de produtos!");
}
return "Produto disponível.";
}
👉 Aqui, se metade das últimas 4 chamadas falharem, o circuito “abre” e as próximas chamadas falham rápido por 5 segundos, sem tentar acessar o serviço.
Fallback – Um Plano B Inteligente
Quando tudo dá errado, ainda é possível responder de forma útil ao usuário.
@Fallback(fallbackMethod = "buscarPrecoCache")
public String obterPreco() {
throw new RuntimeException("Serviço de preços indisponível!");
}
public String buscarPrecoCache() {
return "Preço não disponível no momento. Valor estimado: R$ 120,00";
}
👉 Nesse caso, se o serviço estiver fora do ar, um valor alternativo é retornado ao cliente.
Bulkhead – Controle de Concorrência
Se uma parte do sistema for sobrecarregada por requisições simultâneas, ela pode travar o todo. O bulkhead ajuda a isolar esse impacto.
@Bulkhead(value = 5)
public String processarPedido() {
return "Pedido processado com sucesso.";
}
👉 Apenas 5 execuções paralelas serão permitidas. O restante será enfileirado ou rejeitado, evitando saturar os recursos.
Dicas e Boas Práticas
- ⚡ Combine estratégias: por exemplo,
@Timeoutjunto de@Fallbackgarante que o usuário sempre tenha uma resposta. - ❌ Cuidado com retries excessivos: repetir chamadas sem controle pode aumentar a sobrecarga de serviços já comprometidos.
- 📊 Monitore o comportamento: use métricas expostas pelo Quarkus (via MicroProfile Metrics) e integre com Prometheus/Grafana.
- 🧪 Teste cenários de falha: simule indisponibilidade de banco, lentidão em APIs externas e veja como a aplicação reage.
Conclusão
Projetar sistemas distribuídos exige aceitar que falhas vão acontecer. O que diferencia uma aplicação frágil de uma robusta é a capacidade de se recuperar e continuar operando.
Com o suporte nativo a MicroProfile Fault Tolerance, o Quarkus oferece ferramentas práticas para implementar resiliência de forma simples e eficiente. Usando anotações como @Retry, @Timeout, @CircuitBreaker, @Fallback e @Bulkhead, é possível criar microsserviços mais confiáveis e prontos para rodar em ambientes complexos como a nuvem.
Em resumo: prepare seu sistema para falhar — porque cedo ou tarde, isso vai acontecer.