# Como Implementar Circuit Breaker ao Consumir APIs de CPF em Go

> Aprenda a implementar o padrão circuit breaker em Go para consumir APIs de CPF com resiliência, evitando falhas em cascata e degradação graceful.

**Publicado:** 04/09/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/circuit-breaker-consumir-apis-cpf-go

---


Para consumir APIs de CPF com resiliência em Go, implemente o padrão circuit breaker usando `sync.Mutex` para controle de estado ou a biblioteca `sony/gobreaker` para uma solução pronta para produção. O circuit breaker monitora a taxa de falhas e, ao atingir o limiar configurado, interrompe temporariamente as chamadas à API — retornando uma resposta de fallback ou dado de cache — para evitar que uma falha externa derrube toda a aplicação.

## Introdução

Quando uma API externa fica indisponível, continuar enviando requisições não apenas desperdiça recursos, mas pode causar falhas em cascata que derrubam toda a aplicação. O padrão Circuit Breaker resolve esse problema interrompendo temporariamente as chamadas à API quando detecta uma taxa elevada de falhas, permitindo que o sistema se recupere e que a API tenha tempo para se restabelecer.

---

## Como funciona o circuit breaker

O circuit breaker opera em três estados, alternando entre eles conforme o comportamento da API.

| Estado | Comportamento | Transição |
|---|---|---|
| Fechado (Closed) | Requisições passam normalmente | Abre se falhas excederem o limiar |
| Aberto (Open) | Requisições são rejeitadas imediatamente | Muda para half-open após timeout |
| Semi-aberto (Half-Open) | Permite uma requisição de teste | Fecha se sucesso, abre se falha |

**Fechado** -- o circuito permite todas as requisições e monitora a taxa de falhas.

**Aberto** -- o circuito rejeita requisições sem tentar, retornando um erro imediato ou resposta de fallback.

**Semi-aberto** -- o circuito permite uma requisição teste; se bem-sucedida, retorna ao estado fechado.

---

## Implementação manual do circuit breaker

Uma implementação manual oferece controle total sobre o comportamento do circuit breaker.

```go
package circuitbreaker

import (
 "errors"
 "sync"
 "time"
)

type Estado int

const (
 Fechado Estado = iota
 Aberto
 SemiAberto
)

func (e Estado) String() string {
 nomes := [...]string{"Fechado", "Aberto", "SemiAberto"}
 return nomes[e]
}

var (
 ErrCircuitoAberto = errors.New("circuito aberto: requisicoes bloqueadas")
)

type CircuitBreaker struct {
 mu sync.Mutex
 estado Estado
 falhasConsecutivas int
 limiarFalhas int
 timeoutAberto time.Duration
 ultimaFalha time.Time
 totalFalhas int64
 totalSucessos int64
}

func New(limiarFalhas int, timeoutAberto time.Duration) *CircuitBreaker {
 return &CircuitBreaker{
 estado: Fechado,
 limiarFalhas: limiarFalhas,
 timeoutAberto: timeoutAberto,
 }
}

func (cb *CircuitBreaker) Executar(fn func() error) error {
 cb.mu.Lock()

 switch cb.estado {
 case Aberto:
 if time.Since(cb.ultimaFalha) > cb.timeoutAberto {
 cb.estado = SemiAberto
 cb.mu.Unlock()
 return cb.tentarExecucao(fn)
 }
 cb.mu.Unlock()
 return ErrCircuitoAberto

 case SemiAberto:
 cb.mu.Unlock()
 return cb.tentarExecucao(fn)

 default: // Fechado
 cb.mu.Unlock()
 return cb.tentarExecucao(fn)
 }
}

func (cb *CircuitBreaker) tentarExecucao(fn func() error) error {
 err := fn()

 cb.mu.Lock()
 defer cb.mu.Unlock()

 if err != nil {
 cb.falhasConsecutivas++
 cb.totalFalhas++
 cb.ultimaFalha = time.Now()

 if cb.falhasConsecutivas >= cb.limiarFalhas {
 cb.estado = Aberto
 }
 return err
 }

 cb.falhasConsecutivas = 0
 cb.totalSucessos++

 if cb.estado == SemiAberto {
 cb.estado = Fechado
 }
 return nil
}

func (cb *CircuitBreaker) Estado() Estado {
 cb.mu.Lock()
 defer cb.mu.Unlock()
 return cb.estado
}

func (cb *CircuitBreaker) Estatisticas() map[string]interface{} {
 cb.mu.Lock()
 defer cb.mu.Unlock()
 return map[string]interface{}{
 "estado": cb.estado.String(),
 "falhas_consecutivas": cb.falhasConsecutivas,
 "total_falhas": cb.totalFalhas,
 "total_sucessos": cb.totalSucessos,
 }
}
```

---

## Integração com o cliente de CPF

Integre o circuit breaker ao cliente HTTP que consulta a API de CPF.

```go
package main

import (
 "encoding/json"
 "fmt"
 "net/http"
 "time"

 "seu-projeto/circuitbreaker"
)

type CPFClient struct {
 httpClient *http.Client
 apiKey string
 cb *circuitbreaker.CircuitBreaker
}

type ResultadoCPF struct {
 Sucesso bool `json:"sucesso"`
 Nome string `json:"nome,omitempty"`
 Fonte string `json:"fonte"`
 Erro string `json:"erro,omitempty"`
}

func NewCPFClient(apiKey string) *CPFClient {
 return &CPFClient{
 httpClient: &http.Client{Timeout: 10 * time.Second},
 apiKey: apiKey,
 cb: circuitbreaker.New(5, 30*time.Second),
 }
}

func (c *CPFClient) Consultar(cpf string) ResultadoCPF {
 var resultado ResultadoCPF

 err := c.cb.Executar(func() error {
 url := fmt.Sprintf("https://api.cpfhub.io/cpf/%s", cpf)
 req, _ := http.NewRequest("GET", url, nil)
 req.Header.Set("x-api-key", c.apiKey)

 resp, err := c.httpClient.Do(req)
 if err != nil {
 return err
 }
 defer resp.Body.Close()

 if resp.StatusCode >= 500 {
 return fmt.Errorf("servidor retornou %d", resp.StatusCode)
 }

 var apiResp struct {
 Success bool `json:"success"`
 Data struct {
 Name string `json:"name"`
 } `json:"data"`
 }
 json.NewDecoder(resp.Body).Decode(&apiResp)

 resultado = ResultadoCPF{
 Sucesso: apiResp.Success,
 Nome: apiResp.Data.Name,
 Fonte: "api",
 }
 return nil
 })

 if err != nil {
 if err == circuitbreaker.ErrCircuitoAberto {
 return ResultadoCPF{
 Sucesso: false,
 Fonte: "circuit_breaker",
 Erro: "Servico temporariamente indisponivel",
 }
 }
 return ResultadoCPF{
 Sucesso: false,
 Fonte: "erro",
 Erro: err.Error(),
 }
 }

 return resultado
}
```

---

## Usando a biblioteca gobreaker

A biblioteca `sony/gobreaker` oferece uma implementação pronta e testada. O padrão circuit breaker é amplamente documentado em arquitetura de microsserviços — o [OWASP](https://owasp.org/) recomenda sua adoção como controle de resiliência para dependências externas.

```go
package main

import (
 "fmt"
 "time"

 "github.com/sony/gobreaker"
)

func criarCircuitBreaker() *gobreaker.CircuitBreaker {
 config := gobreaker.Settings{
 Name: "cpfhub-api",
 MaxRequests: 3, // Requisições em half-open
 Interval: 60 * time.Second, // Intervalo para resetar contadores
 Timeout: 30 * time.Second, // Tempo no estado aberto

 ReadyToTrip: func(counts gobreaker.Counts) bool {
 taxaFalha := float64(counts.TotalFailures) / float64(counts.Requests)
 return counts.Requests >= 10 && taxaFalha >= 0.6
 },

 OnStateChange: func(name string, from, to gobreaker.State) {
 fmt.Printf("[CircuitBreaker] %s: %s -> %s\n", name, from, to)
 },
 }

 return gobreaker.NewCircuitBreaker(config)
}
```

| Parâmetro | Valor | Descrição |
|---|---|---|
| MaxRequests | 3 | Requisições permitidas em half-open |
| Interval | 60s | Intervalo para resetar contadores em closed |
| Timeout | 30s | Tempo que permanece em open |
| Limiar de trip | 60% de falha em 10+ requests | Quando abrir o circuito |

---

## Fallback e degradação graceful

Quando o circuito está aberto, ofereça respostas de fallback ao invés de simplesmente retornar erro.

```go
func (c *CPFClient) ConsultarComFallback(cpf string) ResultadoCPF {
 resultado := c.Consultar(cpf)

 if resultado.Fonte == "circuit_breaker" {
 // Tentar cache local
 if dadosCache, ok := c.buscarCache(cpf); ok {
 return ResultadoCPF{
 Sucesso: true,
 Nome: dadosCache.Nome,
 Fonte: "cache_fallback",
 }
 }

 // Retornar resposta degradada
 return ResultadoCPF{
 Sucesso: false,
 Fonte: "degradado",
 Erro: "Servico de validacao temporariamente indisponivel. " +
 "O CPF sera validado posteriormente.",
 }
 }

 return resultado
}
```

**Cache como fallback** -- servir dados previamente obtidos quando o circuito está aberto.

**Resposta degradada** -- informar o usuário que a validação ocorrerá posteriormente.

**Fila de reprocessamento** -- enfileirar CPFs que não puderam ser validados para reprocessar quando a API voltar.

---

## Perguntas frequentes

### Qual limiar de falhas configurar no circuit breaker para a API de CPF?
Para a CPFHub.io, um limiar de 5 falhas consecutivas com timeout de 30 segundos é um bom ponto de partida. Em produção, calibre com base no volume de requisições: se o sistema faz menos de 10 chamadas por minuto, falhas consecutivas são um indicador mais confiável que taxa percentual. Para volumes maiores, use a abordagem `gobreaker` com `ReadyToTrip` baseado em taxa (ex.: 60% de falha em 10+ requests).

### O circuit breaker deve ser compartilhado entre goroutines?
Sim, e é exatamente por isso que a implementação usa `sync.Mutex`. Uma única instância de `CircuitBreaker` deve ser criada na inicialização e compartilhada por todas as goroutines que consultam a API. Criar uma instância por goroutine anula o propósito do padrão, já que cada instância teria seu próprio contador de falhas independente.

### Como testar o circuit breaker em ambiente de desenvolvimento?
Use um mock server que retorna erros HTTP 500 para simular indisponibilidade da API. Configure o limiar de falhas para um valor baixo (ex.: 2 falhas) no ambiente de teste, dispare as requisições em sequência e verifique que a terceira retorna `ErrCircuitoAberto` sem chamar o mock server. O `httptest.NewServer` do Go facilita essa abordagem.

### O que acontece com as requisições quando o circuito está semi-aberto?
No estado semi-aberto, apenas uma requisição de teste é permitida. Se ela for bem-sucedida, o circuito fecha e o tráfego normal é retomado. Se falhar, o circuito volta para aberto e reinicia o timeout. Isso evita sobrecarregar uma API que acabou de se recuperar com o volume total de requisições acumulado durante o período de indisponibilidade.

### Leia também

- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [10 erros mais comuns ao integrar uma API de CPF e como evitá-los](https://cpfhub.io/blog/10-erros-mais-comuns-ao-integrar-uma-api-de-cpf)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)

---

## Conclusão

O padrão circuit breaker é essencial para qualquer aplicação que depende de APIs externas como a de CPF. Em Go, a implementação é direta graças às goroutines e mutexes, e bibliotecas como `sony/gobreaker` oferecem soluções prontas para produção. Combinado com fallbacks e degradação graceful, o circuit breaker garante que uma falha na API de CPF não derrube toda a aplicação.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e integre a API de CPF com resiliência na sua aplicação Go.

