Como criar microsserviços resilientes com circuit breaker
Aprenda como funcionam o circuit breaker e o fallback na comunicação síncrona de microsserviços para evitar falhas em cascata em ambientes de produção.
Resumo
- Sistemas distribuídos falham frequentemente devido à alta interdependência entre componentes síncronos.
- O padrão circuit breaker interrompe chamadas repetidas para serviços instáveis, protegendo o sistema de sobrecarga.
- Estratégias de fallback garantem respostas alternativas ou degradação graciosa quando dependências externas caem.
- O monitoramento contínuo de latência e taxas de erro define os limiares ideais de transição de estados.
- Integrar resiliência exige combinar isolamento de falhas com estratégias complementares como limitação de requisições .
O perigo silencioso das falhas em cascata em sistemas distribuídos
Quando separamos uma aplicação monolítica em vários microsserviços, ganhamos agilidade e capacidade de escalar partes específicas de forma isolada. No entanto, introduzimos uma dependência crítica: a comunicação síncrona via rede. Na prática, isso significa que se o serviço de pagamentos demora para responder, a aplicação principal continua acumulando requisições, travando threads e esgotando recursos até que todo o sistema caia como um baralho de cartas.
Essa reação em cadeia é o pesadelo de qualquer engenheiro de software em ambientes de produção. Para combater esse problema, precisamos adotar padrões arquiteturais focados em tolerância a falhas. Assim como um disjuntor elétrico desarma a casa quando há uma sobrecarga para evitar um incêndio, no desenvolvimento de software usamos o mecanismo de circuit breaker para cortar o fluxo de chamadas antes que o caos se instale.
Como funciona o circuit breaker na prática
O circuit breaker opera monitorando continuamente a saúde das chamadas externas feitas para outro serviço. Ele possui essencialmente três estados operacionais: fechado, aberto e meio-aberto. Na prática, quando o disjuntor está fechado, as requisições passam livremente. Se a taxa de erros ou o tempo limite de resposta ultrapassam um limite pré-configurado, o disjuntor muda para o estado aberto.
Com o circuito aberto, novas chamadas para o serviço problemático falham imediatamente de forma local, sem sequer tentar abrir uma conexão de rede inútil. Isso poupa recursos preciosos da CPU e evita que a aplicação dependente continue sobrecarregando um servidor que já está à beira do colapso. Após um período de espera estipulado, o componente entra em estado meio-aberto, permitindo que uma única requisição de teste passe para verificar se o serviço externo se recuperou.
Implementando fallback para garantir degradação graciosa
Cortar o tráfego com o circuit breaker evita a sobrecarga, mas deixa a interface do usuário ou a API consumidora sem uma resposta útil. É aqui que entra o conceito de fallback, que atua como um plano de B estratégico. Na prática, o fallback é uma rota alternativa de código executada sempre que a chamada principal falha ou quando o disjuntor está aberto.
Se um microsserviço de recomendação de produtos falha, por exemplo, o sistema pode retornar uma lista estática de itens mais vendidos ou exibir dados armazenados em cache local em vez de quebrar a página inteira para o cliente. Essa abordagem é conhecida como degradação graciosa: o usuário percebe uma perda menor de funcionalidade, mas o serviço principal permanece totalmente operacional e estável.
Código de exemplo para estruturar a proteção
Para ilustrar a implementação prática, imagine o uso de uma biblioteca de resiliência em um serviço corporativo. O trecho abaixo demonstra a configuração básica de um circuit breaker atrelado a uma função de fallback em ambiente Node.js, protegendo uma chamada HTTP crítica.
const CircuitBreaker = require('opossum');
async function chamarServicoPagamento() {
// Lógica de comunicação síncrona com o serviço externo
return fetch('https://pagamentos.interno/api');
}
const options = {
timeout: 3000, // Se demorar mais de 3 segundos, falha
errorThresholdPercentage: 50, // Se 50% falharem, abre o circuito
resetTimeout: 10000 // Tenta de novo após 10 segundos
};
const breaker = new CircuitBreaker(chamarServicoPagamento, options);
breaker.fallback(() => 'Serviço indisponível no momento. Tente mais tarde.');
breaker.fire()
.then(console.log)
.catch(console.error);Esse código encapsula a chamada de rede de forma que qualquer anomalia seja tratada de imediato. Além de proteger a infraestrutura com o circuit breaker, vale lembrar que a estabilidade geral do sistema se beneficia muito de camadas adicionais de defesa, como o controle rigoroso de requisições na borda para evitar abusos acidentais ou intencionais.
Decisões de design e trade-offs operacionais
Adotar padrões de tolerância a falhas exige escolhas conscientes de arquitetura. O principal trade-off reside na complexidade adicional de monitoramento e no comportamento imprevisível que pode surgir caso os tempos de espera sejam mal configurados. Se o limite de timeout for curto demais, você poderá abrir o circuito prematuramente devido a oscilações normais da rede. Se for longo demais, o sistema continuará travando.
Outro ponto crítico é decidir qual deve ser o comportamento do fallback. Em operações de leitura, retornar cache ou dados padrão é relativamente seguro. Porém, em operações de escrita ou transações financeiras, executar um fallback incorreto pode gerar inconsistências de dados difíceis de rastrear. Por isso, a engenharia deve alinhar com o negócio exatamente qual é o impacto aceitável quando um microsserviço secundário fica inacessível.
Conclusão e próximos passos para produção
Construir microsserviços resilientes não significa apenas escrever código funcional, mas preparar a aplicação para falhar de maneira elegante quando o ambiente ao seu redor colapsar. A combinação correta de circuit breakers, fallbacks inteligentes e monitoramento constante transforma sistemas frágeis em arquiteturas robustas capazes de absorver impactos semderrubar a experiência do usuário final.
À medida que sua infraestrutura evolui, vale a pena expandir essas práticas para outras frentes da engenharia de software, garantindo que cada componente da sua cadeia tecnológica seja construído com a premissa de que a falha na rede é uma certeza inevitável.