Como deixar seu sistema mais resiliente e preparado para falhas?
Resiliência nada mais é do que a capacidade do seu sistema de se recuperar de falhas e continuar funcionando normalmente. Parece óbvio, né? Mas na prática, garantir isso pode ser um desafio. Quem popularizou esse conceito foi Michael Nygard.
Por que o nome Circuit Breaker? 💡
O nome vem de um conceito bem simples: ele funciona exatamente como um disjuntor elétrico. Quando há uma sobrecarga de energia na sua casa, o disjuntor desarma e corta o fluxo para evitar danos. Depois, alguém precisa ir lá e reativá-lo manualmente.
No software, a lógica é parecida. Imagine que seu sistema está recebendo um fluxo alto de requisições e precisa se comunicar com uma API externa. Se essa API começa a demorar demais para responder (timeout de 30 segundos, por exemplo), você pode acabar sobrecarregando seu servidor, causando um efeito dominó que pode levar ao colapso da aplicação.
É aqui que entra o Circuit Breaker. Ele adiciona uma camada de proteção para evitar falhas em cascata. Se uma requisição remota demora muito ou falha, o sistema identifica isso e retorna uma resposta alternativa, evitando que a aplicação fique travada tentando falar com um serviço que não responde.
Estados do Circuit Breaker
O Circuit Breaker pode estar em três estados:
- Closed (Fechado): O fluxo de requisições segue normalmente para a API externa. Tudo certo aqui.
- Open (Aberto): Se as falhas acumularem, o disjuntor abre, impedindo que novas requisições cheguem à API remota. Isso evita que o sistema fique sobrecarregado.
- Half-Open (Semiaberto): Após um tempo, o Circuit Breaker permite algumas requisições para testar se o serviço externo voltou ao normal. Se der certo, ele fecha de novo. Se continuar falhando, volta para o estado aberto.
Você pode estar se perguntando: mas por que existe o estado Half-Open?
No seu disjuntor de casa, depois que ele desarma, alguém precisa ir lá religá-lo manualmente. Mas no software, não faz sentido depender de um ser humano para isso. Então, quando o Circuit Breaker abre (erro detectado), ele espera um tempo antes de tentar se reconectar automaticamente.
Se a API remota voltar a funcionar dentro do tempo configurado (ex: 200ms, 1s etc.), o circuito fecha e tudo volta ao normal. Mas se ainda estiver falhando, ele continua aberto e para de tentar falar com o serviço problemático.
Implementação prática em Node.js
Aqui está um exemplo simples que você pode rodar para ver o Circuit Breaker funcionando na prática.
O que o código faz:
- Um servidor rodando na porta 3000, que faz chamadas para outro servidor na porta 3001 (simulando uma API externa).
- O serviço externo às vezes responde normalmente (200 OK) e às vezes demora ou retorna erro (400), simulando falhas.
- Usamos a biblioteca opossum para gerenciar o Circuit Breaker.
- Se houver falhas acumuladas, o Circuit Breaker abre e para de enviar requisições para a API.
Código:
import express from "express";
import fetch from 'node-fetch';
import CircuitBreaker from 'opossum';
const app = express();
const app2 = express();
// Simula uma requisição para um serviço externo
async function asyncFunctionThatCouldFail() {
return new Promise((resolve, reject) => {
fetch('http://localhost:3001/')
.then(res => res.json())
.then(json => resolve(json))
.catch(err => reject(err));
});
}
// Configuração do Circuit Breaker
const options = {
timeout: 100, // Se a resposta demorar mais que 100ms, falha
errorThresholdPercentage: 50, // Quando 50% das requisições falham, o circuito abre
resetTimeout: 10000 // Após 10s, ele tenta novamente
};
const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options);
// Rota principal que dispara requisições
app.get("/", async (req, response) => {
breaker.fire()
.then(res => response.json(res))
.catch(err => response.status(500).json(err));
});
// Simulando um serviço externo que falha às vezes
app2.get("/", (req, res) => {
const randomNumber = Math.round(Math.random() * 10);
console.log(randomNumber);
if (randomNumber <= 5) {
res.status(200).json({ msg: "Success!" });
} else {
setTimeout(() => {
res.status(400).json({ msg: "Failed!" });
}, 150);
}
});
app.listen(3000, () => console.log(`Listening at http://localhost:3000`));
app2.listen(3001, () => console.log(`Listening at http://localhost:3001`));
// Eventos do Circuit Breaker
breaker.on('open', () => console.log('Circuito ABERTO - Parando requisições! 🚨'));
breaker.on('halfOpen', () => console.log('Circuito SEMIABERTO - Testando novamente... 🔄'));
breaker.on('close', () => console.log('Circuito FECHADO - Tudo normal! ✅'));
Código no Git
Você pode conferir esse código no repositório:
🔗 https://github.com/h1bertobarbosa/circuit-breaker-pattern
Referências utilizadas
🔹 AppSignal - Circuit Breaker em Node.js
🔹 Microsoft - Padrão Circuit Breaker
🔹 Martin Fowler - Explicação detalhada
🔹 Medium - Circuit Breaker para microservices
Top comments (0)