Circuit Breaker: Protegendo Sistemas Distribuídos Contra Falhas em Cascata

Esse conteúdo foi retirado da plataforma https://dinamos.net

Circuit Breaker: Protegendo Sistemas Distribuídos Contra Falhas em Cascata

Introdução

O Circuit Breaker (Disjuntor) é um padrão de design fundamental em sistemas distribuídos, funcionando de maneira análoga a um disjuntor elétrico. Sua principal função é prevenir falhas em cascata, permitindo que sistemas se recuperem automaticamente de situações de erro.

Como Funciona

O Circuit Breaker monitora as chamadas entre serviços e atua como um intermediário que pode interromper o fluxo de requisições quando necessário. Ele opera em três estados distintos:

stateDiagram-v2
[] --> Fechado
Fechado --> Aberto: Muitas falhas
Aberto --> SemiAberto: Timeout
SemiAberto --> Fechado: Sucesso
SemiAberto --> Aberto: Falha

Estados do Circuit Breaker

  1. Fechado (Normal)

    • Estado padrão
    • Requisições passam normalmente
    • Monitora taxa de falhas
    • Conta falhas em uma janela de tempo
  2. Aberto (Bloqueado)

    • Ativado após muitas falhas
    • Rejeita todas as requisições
    • Inicia timer de timeout
    • Falha rápido sem consumir recursos
  3. Semi-Aberto (Teste)

    • Estado de teste após timeout
    • Permite algumas requisições
    • Monitora resultados
    • Decide próximo estado

Implementação

Exemplo de implementação básica em TypeScript:

class CircuitBreaker {
  private state: "CLOSED" | "OPEN" | "HALF_OPEN" = "CLOSED";
  private failureCount: number = 0;
  private failureThreshold: number = 5;
  private resetTimeout: number = 60000; // 60 segundos
  private lastFailureTime: number = 0;
  async execute(operation: () => Promise<any>): Promise<any> {
    if (this.shouldAllowRequest()) {
      try {
        const result = await operation();
        this.onSuccess();
        return result;
      } catch (error) {
        this.onFailure();
        throw error;
      }
    } else {
      throw new Error("Circuit Breaker is OPEN");
    }
  }
  private shouldAllowRequest(): boolean {
    if (this.state === "CLOSED") return true;
    if (this.state === "OPEN") {
      if (Date.now() - this.lastFailureTime >= this.resetTimeout) {
        this.state = "HALF_OPEN";
        return true;
      }
      return false;
    }
    return true; // HALF_OPEN permite algumas requisições
  }
  private onSuccess(): void {
    if (this.state === "HALF_OPEN") {
      this.state = "CLOSED";
      this.failureCount = 0;
    }
  }
  private onFailure(): void {
    this.failureCount++;
    this.lastFailureTime = Date.now();
    if (this.failureCount >= this.failureThreshold) {
      this.state = "OPEN";
    }
  }
}

Configurações Importantes

graph TD
A[Circuit Breaker] --> B[Threshold]
A --> C[Timeout]
A --> D[Fallback]
B --> E[Número de falhas]
B --> F[Janela de tempo]
C --> G[Tempo de reset]
C --> H[Tempo de espera]
D --> I[Resposta padrão]
D --> J[Cache]

Parâmetros Configuráveis

  1. Threshold de Falhas

    • Número de falhas permitidas
    • Janela de tempo para contagem
    • Taxa de erro aceitável
  2. Timeouts

    • Tempo de espera por resposta
    • Período de reset do circuito
    • Intervalo entre tentativas
  3. Estratégias de Fallback

    • Respostas em cache
    • Valores padrão
    • Rotas alternativas

Exemplo do Mundo Real

Considere um serviço de e-commerce com múltiplos microsserviços:

sequenceDiagram
participant Cliente
participant API Gateway
participant Serviço de Produtos
participant Serviço de Preços
participant Circuit Breaker
Cliente->>API Gateway: Busca produto
API Gateway->>Circuit Breaker: Verifica estado
alt Circuit Breaker Fechado
Circuit Breaker->>Serviço de Produtos: Requisição
Serviço de Produtos->>Serviço de Preços: Busca preço
Serviço de Preços-->>Serviço de Produtos: Timeout
Serviço de Produtos-->>Circuit Breaker: Erro
Circuit Breaker->>Circuit Breaker: Incrementa falhas
else Circuit Breaker Aberto
Circuit Breaker-->>API Gateway: Rejeita requisição
API Gateway-->>Cliente: Fallback (cache)

Cenário

  1. O serviço de preços começa a falhar
  2. Circuit Breaker detecta falhas consecutivas
  3. Circuito abre, protegendo o sistema
  4. API retorna preços em cache
  5. Após timeout, testa recuperação

Benefícios

  1. Resiliência

    • Previne falhas em cascata
    • Permite recuperação automática
    • Isola componentes problemáticos
  2. Performance

    • Falha rápido quando necessário
    • Reduz latência em falhas
    • Economiza recursos
  3. Monitoramento

    • Facilita detecção de problemas
    • Fornece métricas claras
    • Ajuda no diagnóstico

Melhores Práticas

  1. Configuração

    • Ajuste thresholds por contexto
    • Use timeouts apropriados
    • Implemente fallbacks relevantes
  2. Monitoramento

    • Monitore estados do circuito
    • Colete métricas de falhas
    • Alerte em mudanças de estado
  3. Implementação

    • Use bibliotecas testadas
    • Implemente por domínio
    • Considere contexto da aplicação

Conclusão

O Circuit Breaker é essencial para construir sistemas distribuídos resilientes. Sua implementação adequada pode prevenir falhas catastróficas e melhorar significativamente a experiência do usuário em cenários de erro.

Recursos Adicionais

Muito massa! Eu implementei isso no programa de fidelidade no ecommerce da empresa onde trabalho. É muito útil e faz com que nao atole um serviço com um momte de requests caso ele caia. Aqui use uma lib pra PHP chamada Ganesha.