Go 1.22 之后标准库的 ServeMux 也支持方法和通配符了,还需要 Gin 吗?这篇不做口号式的“谁更快”,而是先对齐行为差异,再测纯路由匹配的耗时,最后加上 handler 的真实工作,看路由的几十纳秒在整个请求里占多大比重。
GET /users/42 ServeMux → 200 "mux id=42" Gin → 200 "gin id=42"
GET /users/new ServeMux → 200 "mux new" Gin → 200 "gin new"
POST /users/42 ServeMux → 405 "Method Not Allowed" Allow=GET, HEAD Gin → 404 "404 page not found"
GET /users/42/ ServeMux → 404 "404 page not found" Gin → 301 "<a href=\"/users/42\">Moved Permanently</a>." Location=/users/42
GET /files/a/b.txt ServeMux → 200 "mux path=a/b.txt" Gin → 200 "gin path=/a/b.txt"
GET /files/ ServeMux → 200 "mux path=" Gin → 200 "gin path=/"
GET /dir ServeMux → 307 "<a href=\"/dir/\">Temporary Redirect</a>." Location=/dir/ Gin → 301 "<a href=\"/dir/\">Moved Permanently</a>." Location=/dir/
GET /dir/x ServeMux → 200 "mux dir" Gin → 404 "404 page not found"
GET /USERS/42 ServeMux → 404 "404 page not found" Gin → 404 "404 page not found"
HEAD /users/42 ServeMux → 200 "mux id=42" Gin → 404 "404 page not found"
GET /users//42 ServeMux → 307 "<a href=\"/users/42\">Temporary Redirect</a>." Location=/users/42 Gin → 404 "404 page not found"
GET /nope ServeMux → 404 "404 page not found" Gin → 404 "404 page not found"ServeMux 有一些 Gin 默认没有的“自动行为”:方法不匹配返回 405 与 Allow,HEAD 自动匹配 GET 模式,多余斜杠 307 重定向。Gin 则默认 404(需要 HandleMethodNotAllowed)、HEAD 单独注册、尾斜杠 301。通配符的值也不同:ServeMux 的 {path...} 不带开头的斜杠,Gin 的 *path 带。迁移时这些都是需要逐项确认的地方。
[静态路由]
路由数 | http.ServeMux | Gin
1 | 130 ns 0 allocs | 26 ns 0 allocs
100 | 145 ns 0 allocs | 52 ns 0 allocs
1000 | 158 ns 0 allocs | 67 ns 0 allocs
10000 | 162 ns 0 allocs | 88 ns 0 allocs
[带 {id} / :id 的路由]
路由数 | http.ServeMux | Gin
1 | 174 ns 1 allocs | 31 ns 0 allocs
100 | 188 ns 1 allocs | 59 ns 0 allocs
1000 | 199 ns 1 allocs | 78 ns 0 allocs
10000 | 201 ns 1 allocs | 102 ns 0 allocs只测路由匹配(空 handler),Gin 的 radix 树明显更快:1 条静态路由 26 ns 对 130 ns,1 万条带参数的路由 102 ns 对 201 ns;Gin 命中路由 0 次分配,ServeMux 带参数时每次 1 次分配(取参数)。两者随路由数的增长都很缓慢。
第 1 轮:JSON:ServeMux(json.Encoder) 517 ns 7 allocs | Gin c.JSON(gin.H) 589 ns 9 allocs;字符串:ServeMux(Fprintf) 140 ns 2 allocs | Gin c.String 188 ns 3 allocs
第 2 轮:JSON:ServeMux(json.Encoder) 513 ns 7 allocs | Gin c.JSON(gin.H) 591 ns 9 allocs;字符串:ServeMux(Fprintf) 139 ns 2 allocs | Gin c.String 188 ns 3 allocshandler 里一旦做点事,差距就没了:同样写一个小 JSON,ServeMux 配 json.Encoder 517 ns,Gin 的 c.JSON(gin.H) 589 ns,反而慢一点(gin.H 是 map、Context 的封装也有开销)。一个真实服务的单次请求是微秒到毫秒级(数据库、网络),路由那几十纳秒不是选型的理由。选 Gin 的理由应该是中间件、绑定、校验、渲染这些功能,而不是性能。