Vamos sair da teoria e pegar martelo.
Load Testing (Teste de Carga)
Load testing é "vamos ver até onde o sistema aguenta antes de quebrar". Existem três variantes que você precisa conhecer:
| Tipo | Objetivo | Exemplo |
|---|---|---|
| Load test | Validar comportamento sob carga esperada | "Aguentamos 1.000 RPS na Black Friday?" |
| Stress test | Achar o limite do sistema | "Em quantos RPS ele cai?" |
| Soak test (endurance) | Rodar por horas pra detectar memory leaks, conexões vazando, etc. | "Roda 12h em 80% de carga e vê o que acontece" |
| Spike test | Subir carga bruscamente pra ver se aguenta picos | "Vai de 100 pra 5.000 RPS em 10 segundos" |
As ferramentas que valem a pena aprender
- vegeta: em Go, CLI + biblioteca nativa, métricas detalhadas (p95, p99, histograma). Recomendação para projetos Go.
- hey: linha de comando em Go, zero configuração, ideal pra testes rápidos de baseline.
- wrk / wrk2: C, extremamente rápidas, ótimas pra testes de throughput puro.
- k6 (Grafana): usa JavaScript como DSL de testes, leve, boa integração com CI/CD.
- Locust: em Python, ótima pra cenários com lógica de negócio complexa.
- JMeter: veterano em Java, GUI pesada mas extremamente completo.
Exemplo prático com vegeta
// load-test.go — usando github.com/tsenart/vegeta/v12
package main
import (
"fmt"
"time"
vegeta "github.com/tsenart/vegeta/v12/lib"
)
func main() {
rate := vegeta.Rate{Freq: 200, Per: time.Second} // 200 req/s
duration := 2 * time.Minute
targeter := vegeta.NewStaticTargeter(vegeta.Target{
Method: "GET",
URL: "https://api.minha-empresa.com/users/42",
})
attacker := vegeta.NewAttacker()
var metrics vegeta.Metrics
for res := range attacker.Attack(targeter, rate, duration, "load-test") {
metrics.Add(res)
}
metrics.Close()
fmt.Printf("p95: %s\n", metrics.Latencies.P95)
fmt.Printf("p99: %s\n", metrics.Latencies.P99)
fmt.Printf("RPS: %.2f\n", metrics.Rate)
fmt.Printf("erros: %.2f%%\n", (1-metrics.Success)*100)
}Roda com go run load-test.go e entrega p95, p99, RPS e taxa de erro. Sem escrever código, o CLI resolve: vegeta attack -rate=200 -duration=2m -targets=targets.txt | vegeta report.
localhost mente descaradamente.Profiling em Go
Profiling é colocar o sistema na mesa de cirurgia: você descobre exatamente qual função está consumindo CPU, qual alocando memória, qual segurando lock. Em Go, isso é maravilhosamente integrado via o pacote pprof.
Habilitando o pprof na sua aplicação
package main
import (
"log"
"net/http"
_ "net/http/pprof" // ← este import já registra os handlers em /debug/pprof
)
func main() {
// server de profiling separado, em outra porta (não exponha em produção pública)
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// ... resto da sua app
}Pronto. Sua app agora expõe perfis em http://localhost:6060/debug/pprof/.
Os perfis principais
| Perfil | O que mostra | Quando usar |
|---|---|---|
profile | CPU samples (default 30s) | "Tá lento, onde a CPU tá indo?" |
heap | Alocações de memória vivas | "Memória só sobe, tem leak?" |
allocs | Todas as alocações (vivas + mortas) | "Onde gera lixo demais pro GC?" |
goroutine | Snapshot de todas as goroutines | "Travou? Goroutines vazando?" |
block | Onde goroutines bloqueiam | "Throughput baixo, contenção em locks?" |
mutex | Contenção de mutex | "Mutex segurando demais?" |
Coletando e analisando
# coleta 30s de CPU profile e abre análise interativa
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
# coleta heap (memória atual)
go tool pprof http://localhost:6060/debug/pprof/heap
# UI web (muito mais legível que terminal)
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile?seconds=30Dentro do pprof:
(pprof) top10 # 10 funções que mais consomem
(pprof) list MyFunc # vê o código anotado linha a linha com tempo gasto
(pprof) web # abre gráfico em SVG no browser
Flame graphs
A UI web (-http=:8080) traz o famoso flame graph: cada bloco horizontal é uma função, e a largura é proporcional ao tempo gasto. Você "voa" pelo stack e vê instantaneamente onde está o gargalo.
Benchmarks no Go
Antes de profiling em produção, escreva benchmarks:
// benchmark_test.go
func BenchmarkParseJSON(b *testing.B) {
data := []byte(`{"id":1,"name":"Claude"}`)
b.ResetTimer()
for i := 0; i < b.N; i++ {
var u User
json.Unmarshal(data, &u)
}
}Roda com:
go test -bench=. -benchmem -cpuprofile=cpu.out -memprofile=mem.out
go tool pprof -http=:8080 cpu.outblock e mutex profiling tem overhead. Em produção, ative só durante a investigação ou use sampling baixo.