Quarkus

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 Pattern
@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 Pattern
@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.

Circuit Breaker Pattern
@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 Pattern
@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 Pattern
@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, @Timeout junto de @Fallback garante 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.