Imagine que você está dirigindo um carro e percebe que o motor está superaquecendo. O que você faz? Provavelmente, encosta o carro e espera esfriar antes de continuar a viagem. Caso contrário, insistir poderia fundir o motor e causar um problema muito mais grave.
Essa ideia é semelhante ao conceito de Circuit Breaker. Ele age como um mecanismo de proteção para evitar que falhas temporárias afetem o sistema.
O problema que o Circuit Breaker resolve
Em grande parte das aplicações dos dias de hoje, é comum que um sistema dependa de N serviços, como APIs de terceiros, bancos de dados ou até microserviços internos. O problema é que esses serviços não são infáliveis/resilientes e muitos deles não possuem estratégias/mecanismos de recuperação, algumas possíveis falhas são:
- Excesso de carga
- Problemas de rede
- Bugs
- Manutenção inesperada
Se um serviço continuar enviando requisições a um sistema já sobrecarregado ou falho, pode acabar piorando a situação. Isso pode gerar efeitos cascata, onde uma falha pequena se espalha e derruba toda a infraestrutura ou impacta diretamente o usuário final.
O Circuit Breaker impede isso ao "desligar" temporariamente as requisições para serviços que estão falhando, dando tempo para que eles se recuperem.
Como o Circuit Breaker funciona?
O padrão Circuit Breaker geralmente segue três estados:
1. Fechado (Closed) ✅
No estado normal, todas as requisições passam normalmente. Mas o Circuit Breaker monitora as chamadas para detectar falhas.
2. Aberto (Open) ⛔
Se a taxa de falhas ultrapassar um determinado limite (por exemplo, 50% das requisições falharem em um curto período), o Circuit Breaker abre, ou seja, ele bloqueia todas as novas requisições para evitar sobrecarregar o serviço problemático.
3. Meio Aberto (Half-Open) ⚠️
Depois de um tempo, o Circuit Breaker entra em um estado de "teste", permitindo algumas requisições para verificar se o serviço voltou ao normal. Se as requisições forem bem-sucedidas, ele volta ao estado fechado. Caso contrário, permanece aberto por mais tempo.
Implementando um Circuit Breaker na prática
Existem várias bibliotecas que ajudam a implementar esse padrão, algumas delas são:
- Resilience4j (Java)
- Hystrix (Netflix, mas descontinuado)
- Polly (.NET)
- Opensource-circuitbreaker (Node.js)
Exemplo prático em Node.js com opossum
O opossum
é uma biblioteca simples para implementar Circuit Breaker no Node.js:
import CircuitBreaker from 'opossum';
import axios from 'axios';
async function callExternalService() {
return axios.get('https://api.example.com/data');
}
const breaker = new CircuitBreaker(callExternalService, {
timeout: 3000, // Tempo limite da requisição
errorThresholdPercentage: 50, // Limite de falhas antes de abrir o Circuit Break
resetTimeout: 10000, // Tempo antes de testar novamente
});
breaker.fallback(() => ({ message: 'Serviço fora' }));
breaker.fire()
.then(response => console.log(response.data))
.catch(err => console.log(err.message));
Case real do Circuit Breaker
Imagine que você possui uma aplicação de vendas, e ao cliente efetuar um compra você envia um e-mail para ele confirmando a compra. Como serviço de envio de e-mail você possui uma integração com o SendGrid, porém por algum motivo o serviço do SendGrid caiu e os clientes não estão recebendo o e-mail de confirmação de compra, para contornar isso o Circuit Breaker entra em ação, ao detectar uma quantidade elevada(definida por você) de erros o Circuit Breaker abre e para de enviar e-mail para o serviço do SendGrid, porém você ainda necessita comunicar o cliente da compra e para isso existem N formas de resolver esse problema, aqui segue algumas formas de solução:
- Os e-mail não enviados são inseridos em uma fila, para quando o serviço do SendGrid voltar eles possam ser reprocessados.
- Utilizar como Fallback outro provedor de serviços de envio de e-mail para que o cliente final não seja impactado.
- Caso seja um instabilidade momentanea, implementar retry é simples e eficiente.
A soma dessas soluções tambem trás uma resiliência para aplicação.
Benefícios do Circuit Breaker
- Evita falhas cascata: Protege o sistema como um todo, impedindo que um serviço com defeito derrube outros.
- Melhora a resiliência: Permite que os serviços tenham tempo para se recuperar.
- Reduz o tempo de resposta: Em vez de esperar por timeouts demorados, ele retorna rapidamente com uma resposta alternativa.
- Ajuda na observabilidade: Fornece insights sobre a taxa de falhas dos serviços, auxiliando no monitoramento.
Portanto o Circuit Breaker é um padrão essencial para tornar aplicações, como um todo, mais robustos e resilientes. Ao detectar falhas e agir proativamente, ele impede que pequenos problemas impacte diretamente o cliente final e diminuindo a atuação do seu time em problemas de sustentação da aplicação. Se você trabalha com microsserviços ou APIs externas, considerar um Circuit Breaker é fundamental para garantir a estabilidade da sua aplicação.
Agora que você entende como funciona, que tal implementá-lo no seu próximo projeto? 🚀