Até pouco tempo atrás eu nunca tinha ouvido falar sobre o go-chi. Foi durante uma reunião de trabalho que o Marcos Filho comentou sobre. Na mesma hora eu já anotei aqui na lista de idéias de posts para o blog para fazer uma comparação entre ele e o gorilla/mux.
Meus testes foram basicamente escrever um simples server http com a rota /{name}. Nesse server utilizei a ferramenta wrk para um teste de carga. Também escrevi um benchmark da própria linguagem para verificar, além de quanta porrada ele aguenta, o quanto de recurso os routers consumem.
Para fica mais simples, vou separar as comparações em três partes, onde nas duas primeiras vou apresentar os resultados individuais e por fim uma conclusão.
Então, para começar, vamos ver os resultados do router mais famoso do mundo Go.
Gorilla Mux
Primeiramente, vamos começar pelo código escrito para testar o queridinho Gorilla/mux.
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/{name}", GorillaHelloWorld).Methods("GET")
http.ListenAndServe(":3000", r)
}
func GorillaHelloWorld(w http.ResponseWriter, rq *http.Request) {
vars := mux.Vars(rq)
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": fmt.Sprintf("Olá %s!!!", vars["name"]),
})
}
Com o servidor escrito, levantei ele com um go run e depois executei o seguinte comando para fazer um teste de carga.
$ wrk -t12 -c400 -d30s http://localhost:3000/Tiago+Temporin
O resultado para essa execução foi o seguinte:

Ou seja, conseguimos atender 477.072 requests em 30s, uma média de 15.871 request/sec.
Com essa informação na mão, escrevi um pequeno benchmark para medir quanto recurso ele consome para conseguir chegar a esses números.
func BenchmarkGorilla(b *testing.B) {
for n := 0; n < b.N; n++ {
req, err := http.NewRequest("GET", "/Tiago", nil)
if err != nil {
b.Fatal(err)
}
rr := httptest.NewRecorder()
router := mux.NewRouter()
router.HandleFunc("/{name}", GorillaHelloWorld).Methods("GET")
router.ServeHTTP(rr, req)
}
}
Para testa-lo, executei um go test -bench=. -cpu=8 -benchmem -benchtime=5s -count 5, que gerou o seguinte resultado:

Como podemos ver no resultado, o número de Bytes/Operação e Alocações/Operação (duas últimas colunas) se mantiveram iguais em todas as execuções.
Chi
Assim como fizemos para o Gorilla/mux, vamos iniciar com o código que foi escrito para o server.
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
r.Get("/{name}", ChiHelloWorld)
http.ListenAndServe(":3000", r)
}
func ChiHelloWorld(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": fmt.Sprintf("Olá %s!!!", name),
})
}
Como podemos ver, assim como o do Gorilla/mux, um server bem simples para responder um “Olá {name}!!!”.
Com a implementação feita, depois foi só executar o go run e o mesmo comando do wrk para o teste de carga. O resultado foi o seguinte:

Como podemos ver no print, o Chi conseguiu atender 531.159 requests em 30s com uma média de 17.648 request/sec.
Agora, vamos ver o código utilizado para realizar o benchmark.
func BenchmarkChi(b *testing.B) {
for n := 0; n < b.N; n++ {
req, err := http.NewRequest("GET", "/Tiago", nil)
if err != nil {
b.Fatal(err)
}
rr := httptest.NewRecorder()
router := chi.NewRouter()
router.Get("/{name}", ChiHelloWorld)
router.ServeHTTP(rr, req)
}
}
Na sequência, executei o mesmo comando de benchmark que foi executado para o gorilla/mux. O resultado foi o seguinte:

Assim como podemos ver no primeiro benchmark, o Chi também manteve a mesma quantidade de Bytes/Operações e Alocações/Operações em cada um dos testes executados.
Conclusão
Depois de executar todos esses testes, podemos ver uma clara vantagem para o router Chi.
Em relação ao teste de carga, o Chi conseguiu atender cerca de 11% mais requests.
Na questão do benchmark que fizemos, o Chi utilizou apenas 47% da quantidade de Bytes/Operação e fez 1/3 das alocações de memória por operação quando comparado com o gorilla/mux, o que na minha opinião é uma BAITA diferença.
Em resumo, Chi se mostrou ser um router com uma performance superior consumindo menos recursos, ou seja, o ideal dos mundos.
Deixem suas dúvidas nos comentários.
Até a próxima!
Faça parte da comunidade!
Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.
Livros Recomendados
Abaixo listei alguns dos melhores livros que já li sobre GO.





[…] de escrever APIs. Porém, desde que fiz o post sobre benchmark comparando gorilla/mux e go-chi (link para o post), meu router favorito tem sido o go-chi, pois sua performance é bem superior. E para ajudar, […]