Vamos juntar tudo num projeto pequeno mas realista. Se você seguir, sai com a apostila aplicada.
Cenário
Você tem uma API em Go que expõe GET /products/:id. Ela consulta um banco PostgreSQL e, se não achar, chama uma API externa (api-catalogo.com) que é lenta e instável.
Sua missão:
- Subir a API e dependências localmente.
- Testar carga e medir latência.
- Adicionar timeout, retry e circuit breaker na chamada externa.
- Simular falhas (engenharia do caos baby mode).
- Comparar antes e depois.
Etapa 1 — Suba o ambiente
Use Docker Compose:
# docker-compose.yml
version: '3.8'
services:
api:
build: .
ports: ["8080:8080", "6060:6060"] # 6060 é o pprof
environment:
EXTERNAL_API: http://toxiproxy:8666
DB_DSN: postgres://app:app@db:5432/app?sslmode=disable
depends_on: [db, toxiproxy]
db:
image: postgres:16
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: app
toxiproxy:
image: ghcr.io/shopify/toxiproxy
ports: ["8474:8474", "8666:8666"] # 8474 é a admin APIToxiproxy é um proxy TCP que injeta caos: latência, timeouts, conexões cortadas. Vai ser nosso macaco do caos no nível pobre.
Etapa 2 — Teste de carga inicial
Use hey (escrito em Go) para o teste de carga:
# Instale: go install github.com/rakyll/hey@latest
# warm-up: 30s com 10 workers
hey -z 30s -c 10 http://localhost:8080/products/1
# carga sustentada: 2min com 50 workers
hey -z 2m -c 50 http://localhost:8080/products/1
# ramp-down: 30s com 10 workers
hey -z 30s -c 10 http://localhost:8080/products/1Execute cada etapa em sequência.
Anote os números: p50, p95, p99, RPS, taxa de erro. Esse é seu baseline.
Etapa 3 — Profiling
Com o teste rodando, em outro terminal:
go tool pprof -http=:9090 http://localhost:6060/debug/pprof/profile?seconds=30Abra http://localhost:9090, veja o flame graph, identifique funções lentas. Provavelmente vai ver tempo gasto em I/O da API externa.
Etapa 4 — Aplique os padrões de resiliência
Em pseudo-código Go:
// 1. Timeout via context
ctx, cancel := context.WithTimeout(r.Context(), 1500*time.Millisecond)
defer cancel()
// 2. Circuit breaker (sony/gobreaker)
result, err := cb.Execute(func() (interface{}, error) {
// 3. Retry com backoff exponencial
return doWithRetry(ctx, 3, func() (interface{}, error) {
return fetchExternal(ctx, id)
})
})
// 4. Fallback: se tudo falhou, retorna do cache local ou um valor mínimo
if err != nil {
if cached := localCache.Get(id); cached != nil {
return cached, nil
}
return nil, ErrServiceUnavailable
}Etapa 5 — Engenharia do Caos com Toxiproxy
Crie o "proxy" pra API externa:
curl -X POST http://localhost:8474/proxies -d '{
"name": "external_api",
"listen": "0.0.0.0:8666",
"upstream": "api-catalogo.com:443",
"enabled": true
}'Agora injete caos:
# Adiciona 2s de latência em todas as conexões
curl -X POST http://localhost:8474/proxies/external_api/toxics -d '{
"name": "lag",
"type": "latency",
"attributes": {"latency": 2000}
}'
# Corta 50% das conexões
curl -X POST http://localhost:8474/proxies/external_api/toxics -d '{
"name": "drop",
"type": "limit_data",
"attributes": {"bytes": 0}
}'Rode o k6 de novo. Compare:
- Sem resiliência: sua API trava, p99 explode, taxa de erro vai pras alturas.
- Com resiliência: circuit breaker abre, requisições falham rápido (fail-fast), p99 fica controlado, fallback responde com cache. Vida que segue.
Etapa 6 — Monitoramento
Adicione o handler /metrics (Prometheus), suba um Grafana, plote:
- p50/p95/p99 por endpoint.
- RPS e taxa de erros.
- Estado do circuit breaker (CLOSED/OPEN/HALF-OPEN).
Configure 1 alerta: "taxa de erros > 5% por 5min".
Parabéns: você implementou os 4 sinais dourados, 3 padrões de resiliência, e fez seu primeiro experimento de caos. Já está acima de 70% dos backends do mercado.