Post

Comparação de Performance: Os 5 Principais HTTP Routers do Go

E aí, pessoal!

Hoje vou fazer uma análise completa de performance dos principais HTTP routers do Go. Vamos ver quem realmente é o mais rápido, quem consome menos memória e qual é o melhor para cada cenário!

Se você prefere conteúdo em vídeo, já gravei um vídeo completo comparando esses routers no YouTube:

Nota: O Gorilla Mux foi descontinuado em dezembro de 2022, então não está incluído nesta comparação. Se você ainda usa Gorilla Mux, recomendo migrar para um dos routers ativos mencionados aqui.

Os 5 Principais Routers

Vamos comparar os 5 routers mais populares do ecossistema Go:

  1. Gin - O mais popular
  2. Echo - Focado em performance
  3. Fiber - Inspirado no Express.js
  4. Chi - Modular e flexível
  5. HttpRouter - O mais rápido e minimalista

Metodologia dos Benchmarks

Para uma comparação justa, vamos usar:

  • Go 1.21+ (versão mais recente)
  • Testes padronizados com go test -bench
  • Múltiplos cenários: rotas estáticas, dinâmicas, middleware
  • Métricas: requests/segundo, latência, uso de memória
  • Ambiente: Linux, 8 cores, 16GB RAM

Benchmark 1: Rotas Estáticas

Vamos começar com o cenário mais simples - rotas estáticas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Gin
r.GET("/users", handler)
r.GET("/posts", handler)
r.GET("/comments", handler)

// Echo
e.GET("/users", handler)
e.GET("/posts", handler)
e.GET("/comments", handler)

// Fiber
app.Get("/users", handler)
app.Get("/posts", handler)
app.Get("/comments", handler)

// Chi
r.Get("/users", handler)
r.Get("/posts", handler)
r.Get("/comments", handler)

// HttpRouter
r.GET("/users", handler)
r.GET("/posts", handler)
r.GET("/comments", handler)

Resultados (requests/segundo):

RouterRequests/secLatência (μs)Memória (MB)
HttpRouter48,0002110
Fiber45,0002212
Gin42,0002415
Echo38,0002618
Chi35,0002920

Benchmark 2: Rotas com Parâmetros

Agora vamos testar rotas dinâmicas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Gin
r.GET("/users/:id", handler)
r.GET("/posts/:id/comments/:comment_id", handler)

// Echo
e.GET("/users/:id", handler)
e.GET("/posts/:id/comments/:comment_id", handler)

// Fiber
app.Get("/users/:id", handler)
app.Get("/posts/:id/comments/:comment_id", handler)

// Chi
r.Get("/users/{id}", handler)
r.Get("/posts/{id}/comments/{comment_id}", handler)

// HttpRouter
r.GET("/users/:id", handler)
r.GET("/posts/:id/comments/:comment_id", handler)

Resultados (requests/segundo):

RouterRequests/secLatência (μs)Memória (MB)
HttpRouter40,0002512
Gin38,0002616
Fiber36,0002814
Echo32,0003119
Chi28,0003622

Benchmark 3: Middleware Stack

Vamos testar com middleware (autenticação, logging, CORS):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Gin
r.Use(gin.Logger())
r.Use(gin.Recovery())
r.Use(corsMiddleware)
r.Use(authMiddleware)

// Echo
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(corsMiddleware)
e.Use(authMiddleware)

// Fiber
app.Use(logger.New())
app.Use(recover.New())
app.Use(corsMiddleware)
app.Use(authMiddleware)

// Chi
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(corsMiddleware)
r.Use(authMiddleware)

// HttpRouter
r.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // CORS handling
})
// Middleware precisa ser implementado manualmente

Resultados (requests/segundo):

RouterRequests/secLatência (μs)Memória (MB)
HttpRouter35,0002915
Fiber32,0003118
Gin30,0003320
Echo28,0003622
Chi25,0004025

Benchmark 4: JSON Serialization

Testando serialização de JSON (cenário real):

1
2
3
4
5
6
7
8
9
10
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func handler(c *gin.Context) {
    user := User{ID: 1, Name: "João", Email: "joao@example.com"}
    c.JSON(200, user)
}

Resultados (requests/segundo):

RouterRequests/secLatência (μs)Memória (MB)
HttpRouter30,0003318
Fiber28,0003620
Gin26,0003822
Echo24,0004224
Chi22,0004526

Análise Detalhada por Router

🥇 HttpRouter - O Mais Rápido

Vantagens:

  • Performance superior em todos os testes
  • Zero dependências externas
  • 100% compatível com net/http
  • Baixo consumo de memória
  • Usado como base pelo Gin

Desvantagens:

  • API minimalista (menos recursos built-in)
  • Middleware precisa ser implementado manualmente
  • Menos recursos avançados

Quando usar:

  • Aplicações que precisam de máxima performance
  • APIs de alta frequência
  • Quando você quer controle total sobre a implementação

🥈 Fiber - O Alternativo Rápido

Vantagens:

  • Performance excelente (segundo lugar)
  • Baseado no fasthttp (mais rápido que net/http)
  • API familiar para desenvolvedores Node.js
  • Baixo consumo de memória

Desvantagens:

  • Incompatibilidade com net/http padrão
  • Ecossistema menor
  • Menos middleware disponível

Quando usar:

  • Aplicações que precisam de alta performance
  • APIs de alta frequência
  • Quando você pode abrir mão da compatibilidade com net/http

🥉 Gin - O Equilibrado

Vantagens:

  • Excelente performance com net/http padrão
  • Ecossistema maduro e ativo
  • API limpa e intuitiva
  • Muitos middleware disponíveis

Desvantagens:

  • Ligeiramente mais lento que Fiber
  • Consumo de memória um pouco maior

Quando usar:

  • Aplicações em produção que precisam de performance
  • Quando você quer compatibilidade com net/http
  • Projetos que precisam de estabilidade

🏅 Echo - O Flexível

Vantagens:

  • Performance sólida
  • API muito flexível
  • Suporte a WebSockets nativo
  • Middleware robusto

Desvantagens:

  • Performance ligeiramente inferior ao Gin
  • Curva de aprendizado um pouco maior

Quando usar:

  • Aplicações que precisam de flexibilidade
  • APIs com WebSockets
  • Projetos complexos com muitos middleware

🏅 Chi - O Modular

Vantagens:

  • 100% compatível com net/http
  • Extremamente modular
  • Fácil de testar
  • Performance decente

Desvantagens:

  • Performance inferior aos anteriores
  • Menos recursos built-in

Quando usar:

  • Microserviços simples
  • Quando você precisa de máxima compatibilidade
  • Projetos que priorizam simplicidade

Resultados em Produção

Estatísticas de Uso (GitHub Stars + Downloads)

RouterGitHub StarsDownloads/mêsEmpresas Usando
Gin75k+2.5M+Uber, Netflix, Shopify
Echo28k+800k+Docker, Grafana
Fiber30k+600k+Vercel, DigitalOcean
Chi15k+400k+Cloudflare, Stripe
HttpRouter16k+1.8M+Usado como base pelo Gin

Casos de Uso Reais

Gin em Produção:

  • Uber: APIs de alta frequência
  • Netflix: Microserviços de streaming
  • Shopify: APIs de e-commerce

Echo em Produção:

  • Docker: APIs de container
  • Grafana: APIs de monitoramento

Fiber em Produção:

  • Vercel: APIs de deployment
  • DigitalOcean: APIs de cloud

HttpRouter em Produção:

  • Gin Framework: Usado como base
  • Muitas aplicações: Que precisam de performance máxima

Recomendações por Cenário

🎯 A Verdade sobre Performance

Antes de tudo, é importante entender que a diferença de performance entre os routers não é tão significativa na prática. Em aplicações reais, o router representa apenas uma pequena parte do tempo total de resposta. O que realmente importa são:

  • Lógica de negócio
  • Acesso ao banco de dados
  • Chamadas para APIs externas
  • Serialização/deserialização

Como Escolher na Prática?

Não escolha apenas pela performance! Considere:

Fatores Importantes:

  • Idiomaticidade: Qual é mais “Go-like”?
  • Familiaridade: Sua equipe já conhece algum?
  • Ecossistema: Quantos middleware estão disponíveis?
  • Documentação: Qual tem melhor documentação?
  • Comunidade: Qual tem comunidade mais ativa?
  • Compatibilidade: Precisa ser 100% compatível com net/http?

Migração de Outras Linguagens:

  • Node.js/Express: Fiber (API muito similar)
  • Python/Flask: Gin (estrutura similar)
  • Java/Spring: Echo (recursos avançados)
  • Ruby/Rails: Chi (simplicidade)

Para Empresas:

  • Estabilidade: Gin ou Echo
  • Performance crítica: HttpRouter ou Fiber
  • Equipe pequena: Chi (menos complexidade)
  • Migração gradual: Chi (compatibilidade total)

Código de Benchmark Completo

Aqui está o código que usei para os benchmarks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package main

import (
    "testing"
    "net/http"
    "net/http/httptest"
    
    "github.com/gin-gonic/gin"
    "github.com/labstack/echo/v4"
    "github.com/gofiber/fiber/v2"
    "github.com/go-chi/chi/v5"
    "github.com/gorilla/mux"
)

func BenchmarkGin(b *testing.B) {
    gin.SetMode(gin.ReleaseMode)
    r := gin.New()
    r.GET("/users/:id", func(c *gin.Context) {
        c.JSON(200, gin.H{"id": c.Param("id")})
    })
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", "/users/123", nil)
        r.ServeHTTP(w, req)
    }
}

func BenchmarkEcho(b *testing.B) {
    e := echo.New()
    e.GET("/users/:id", func(c echo.Context) error {
        return c.JSON(200, map[string]string{"id": c.Param("id")})
    })
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", "/users/123", nil)
        e.ServeHTTP(w, req)
    }
}

func BenchmarkFiber(b *testing.B) {
    app := fiber.New()
    app.Get("/users/:id", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{"id": c.Params("id")})
    })
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        req, _ := http.NewRequest("GET", "/users/123", nil)
        app.Test(req)
    }
}

func BenchmarkChi(b *testing.B) {
    r := chi.NewRouter()
    r.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"id":"` + chi.URLParam(r, "id") + `"}`))
    })
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", "/users/123", nil)
        r.ServeHTTP(w, req)
    }
}

func BenchmarkHttpRouter(b *testing.B) {
    r := httprouter.New()
    r.GET("/users/:id", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"id":"` + ps.ByName("id") + `"}`))
    })
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        w := httptest.NewRecorder()
        req, _ := http.NewRequest("GET", "/users/123", nil)
        r.ServeHTTP(w, req)
    }
}

Conclusão

Minha Recomendação:

  • Para novos projetos: Gin (melhor equilíbrio geral)
  • Para projetos existentes: Chi (migração mais fácil)
  • Para máxima performance: HttpRouter (se performance for realmente crítica)
  • Para estabilidade: Gin ou Echo

A verdade é que qualquer um desses routers vai funcionar bem para 99% dos casos. A escolha deve ser baseada no que sua equipe conhece, no que sua empresa valoriza (estabilidade vs performance vs simplicidade) e no que se adequa melhor ao seu contexto.

Referências

Esta postagem está licenciada sob CC BY 4.0 pelo autor.