Circuit breaker: pare de insistir num serviço que caiu
Entenda como o padrão circuit breaker evita que a falha de uma API externa derrube também o seu sistema, os três estados (fechado, aberto e meio-aberto) e uma implementação simples em Node.
Quando uma API externa fica fora do ar, o instinto natural do código é tentar de novo. Um retry resolve falha momentânea, mas quando o serviço caiu de verdade, insistir só piora a situação, e não apenas pra ele: o seu próprio sistema começa a afundar junto.
Como uma falha externa vira uma falha sua
Imagina uma rota que chama um serviço de cálculo de frete antes de fechar o pedido:
app.post("/pedidos", async (req, res) => {
const frete = await fetch("https://api-frete.com/calcular", {
method: "POST",
body: JSON.stringify(req.body),
});
// ...
});Se esse serviço ficar lento ou parar de responder, cada requisição à sua rota fica pendurada esperando o timeout. Com tráfego real, em poucos segundos há centenas de requisições presas, consumindo conexões e memória do seu servidor. Como o event loop do Node continua atendendo o resto, a degradação é gradual: o sistema não cai de uma vez, ele fica cada vez mais lento até derrubar rotas que nem dependem do frete. É a falha em cascata.
A ideia do circuit breaker
O nome vem do disjuntor elétrico: quando detecta um problema, ele desarma e corta o circuito, protegendo o resto da instalação. No código, o circuit breaker observa as chamadas a um serviço e, depois de falhas suficientes, para de chamar por um tempo, respondendo erro imediatamente em vez de esperar um timeout que já se sabe que vai acontecer.
Ele funciona com três estados:
- Fechado: tudo normal, as chamadas passam e as falhas são contadas.
- Aberto: o limite de falhas foi atingido, as chamadas são recusadas na hora, sem nem tentar o serviço.
- Meio-aberto: passado um tempo de espera, uma única chamada de teste passa. Se der certo, volta pra fechado. Se falhar, volta pra aberto.
Uma implementação simples
class CircuitBreaker {
constructor(acao, { limiteFalhas = 5, tempoEspera = 10000 } = {}) {
this.acao = acao;
this.limiteFalhas = limiteFalhas;
this.tempoEspera = tempoEspera;
this.falhas = 0;
this.estado = "fechado";
this.reabreEm = 0;
}
async executar(...args) {
if (this.estado === "aberto") {
if (Date.now() < this.reabreEm) {
throw new Error("circuito aberto");
}
this.estado = "meio-aberto";
}
try {
const resultado = await this.acao(...args);
this.falhas = 0;
this.estado = "fechado";
return resultado;
} catch (erro) {
this.falhas++;
if (this.estado === "meio-aberto" || this.falhas >= this.limiteFalhas) {
this.estado = "aberto";
this.reabreEm = Date.now() + this.tempoEspera;
}
throw erro;
}
}
}E o uso, embrulhando a chamada que pode falhar:
const calcularFrete = new CircuitBreaker(async (dados) => {
const res = await fetch("https://api-frete.com/calcular", {
method: "POST",
body: JSON.stringify(dados),
signal: AbortSignal.timeout(2000),
});
if (!res.ok) throw new Error("falha no frete");
return res.json();
});Repara no AbortSignal.timeout(2000): o circuit breaker conta falhas, mas só funciona bem se cada chamada falha rápido. Sem um timeout curto, o problema original (requisições penduradas) continua existindo.
O que fazer enquanto o circuito está aberto
Recusar rápido é só metade da solução. A outra metade é decidir o que o usuário vê. Algumas opções comuns:
- Devolver um valor padrão ou em cache (um frete estimado em vez do calculado).
- Desligar só aquela funcionalidade e deixar o resto do sistema funcionando.
- Enfileirar a operação pra processar depois, como no post sobre filas de mensagem.
O ganho está em escolher essa degradação de forma consciente, em vez de deixar o sistema inteiro cair por causa de uma dependência.
Fechando
Circuit breaker não evita que o serviço externo caia, evita que a queda dele se torne sua. Em troca de um pouco de código a mais, você ganha falha rápida, um sistema que se mantém de pé parcialmente e um serviço externo com espaço pra se recuperar, sem receber uma enxurrada de tentativas enquanto tenta voltar.
Direto na sua
caixa de entrada.
Um aviso por e-mail sempre que eu publicar um post novo. Sem spam, sem newsletter chata, só isso.
Leia também
- HTTP/1.1, HTTP/2 e HTTP/3: o que mudou de verdade30 de outubro de 2026 · 4 min
- Opus 5.5 e HyperFrames: edição de vídeo com um prompt29 de outubro de 2026 · 5 min
- Opus 5.5 no Claude Code: 12 dicas oficiais da Anthropic28 de outubro de 2026 · 7 min
- SSR, SSG e ISR: como escolher a renderização certa28 de outubro de 2026 · 4 min