Marcio Cunha

Microsserviços instáveis? Como usar circuit breaker e fallbacks

Descubra como proteger sua arquitetura distribuída contra falhas em cascata usando o padrão circuit breaker e respostas de fallback sob alta carga.

Marcio Cunha•4 min
Também disponível em:EnglishEspañol
Resumo
  • O padrão circuit breaker interrompe chamadas a serviços instáveis para proteger o ecossistema contra falhas em cascata.
  • Respostas de fallback garantem que o sistema continue operacional mesmo quando dependências críticas saem do ar.
  • Monitorar taxas de erro e latência permite ajustar a sensibilidade do disjuntor conforme o comportamento do tráfego.
  • Estratégias consistentes de transação distribuída evitam estados inconsistentes em falhas de rede, conforme visto em consistência transacional com Saga .
  • Testar falhas simuladas em ambiente controlado assegura que os mecanismos de recuperação respondem corretamente sob pressão.

Por que microsserviços caem juntos sob alta carga?

Quando separamos um sistema grande em vários blocos menores chamados microsserviços, ganhamos liberdade para escalar partes isoladas. No entanto, criamos uma rede complexa de dependências onde uma única falha pode se espalhar rapidamente. Se o serviço de pagamento trava por lentidão no banco de dados, as requisições começam a se acumular nas filas de espera. Na prática, isso significa que threads e conexões de rede esgotam-se em cadeia, derrubando toda a aplicação em poucos minutos por causa de um gargalo localizado.

Em arquiteturas modernas, o maior perigo não é apenas o serviço falhar, mas a forma como os sistemas clientes reagem a essa indisponibilidade. Sem mecanismos de proteção, cada nova tentativa de requisição consome recursos preciosos de CPU e memória enquanto espera por uma resposta que nunca chega. É exatamente para resolver esse problema de esgotamento de recursos que aplicamos padrões defensivos de engenharia de software.

O que é um circuit breaker e como ele funciona?

Inspirado nos disjuntores elétricos da nossa casa que desligam a energia quando há sobrecarga, o circuit breaker é um componente de software que monitora chamadas entre serviços. Ele opera basicamente em três estados: fechado, aberto e meio-aberto. No estado fechado, as requisições fluem normalmente para o serviço externo. Quando a quantidade de erros ultrapassa um limite tolerável, o disjuntor abre, bloqueando imediatamente novas tentativas de chamada e evitando desperdício de tempo e recursos.

Enquanto o circuito permanece aberto, qualquer chamada ao serviço defeituoso falha de imediato, retornando uma resposta alternativa pré-programada. Após um período de tempo pré-determinado, o disjuntor passa para o estado meio-aberto, permitindo que apenas uma requisição teste o terreno. Se essa requisição passar com sucesso, o circuito fecha novamente; se falhar, ele retorna ao estado aberto. Essa dinâmica protege tanto o cliente quanto o servidor sobrecarregado, dando tempo para que o sistema se recupere.

Implementando fallbacks inteligentes para manter o sistema vivo

Bloquear uma requisição com o circuit breaker resolve o problema do esgotamento de recursos, mas deixa o usuário final sem resposta se não houver um plano alternativo. É aqui que entram os fallbacks, que funcionam como um plano B operacional quando a ferramenta principal falha. Em vez de exibir uma tela de erro genérica ou quebrar a página inteira, o sistema entrega dados em cache, informações parciais ou uma mensagem amigável informando instabilidade temporária.

Para garantir que esse fluxo funcione sem corromper dados transacionais complexos, muitas equipes combinam disjuntores com abordagens de resiliência, alinhando-se a estratégias robustas como a gestão de consistência transacional em microsserviços. O segredo de um bom fallback é equilibrar utilidade e simplicidade, garantindo que a rota alternativa seja rápida, leve e independente do componente que acabou de falhar.

Exemplo prático de circuit breaker em código

Para entender como isso funciona na prática, podemos observar uma implementação básica utilizando bibliotecas modernas de resiliência em linguagens de backend. O código abaixo demonstra como configurar um disjuntor que monitora falhas de chamada e aciona uma resposta de fallback automática caso o serviço externo demore muito ou retorne erros consecutivos.

const { CircuitBreaker } = require('opossum');

async function chamarServicoPagamento(dados) {
  // Simula uma chamada de API externa que pode falhar
  if (Math.random() > 0.5) throw new Error('Serviço indisponível');
  return 'Pagamento aprovado';
}

const options = {
  timeout: 3000,
  errorThresholdPercentage: 50,
  resetTimeout: 10000
};

const breaker = new CircuitBreaker(chamarServicoPagamento, options);

breaker.fallback(() => 'Sistema instável. Tente novamente mais tarde.');

breaker.fire({ id: 123 })
  .then(console.log)
  .catch(console.error);

No exemplo acima, configuramos um tempo limite de três segundos e definimos que, se cinquenta por cento das requisições falharem, o disjuntor abre por dez segundos. Durante esse intervalo, a função de fallback assume o controle imediatamente. Isso impede que picos de lentidão externa paralisem todo o microsserviço principal, preservando a experiência geral do usuário. Para complementar essa implementação em produção, vale conferir também nossas análises técnicas sobre Ruído eletromagnético na placa? Como eliminar EMI no roteamento e Servidor lento? Como achar gargalos de disco e IOPS no Linux.

Considerações finais sobre resiliência distribuída

Desenvolver aplicações resilientes exige uma mudança de mentalidade onde assumimos que falhas de rede e quedas de servidores são eventos normais, e não exceções improváveis. O uso combinado de circuit breakers e fallbacks transforma sistemas frágeis em estruturas capazes de absorver impactos sem colapsar. Ao planejar sua arquitetura, lembre-se de que a estabilidade de um sistema distribuído depende tanto da robustez dos serviços principais quanto da inteligência com que ele lida com a indisponibilidade alheia.