Go Web 框架怎么选

你好,我是 Bobby。

我们在 Go 写 Web 服务时,经常会遇到一个问题:用标准库就够了,还是直接上 Gin、Echo、Fiber 这类框架?

JetBrains 在 2026 年 4 月发了一篇 Go Web 框架梳理文章,里面引用了两组数据。

  • Go 官方 2025 年开发者调研显示,46% 的 Go 开发者会用 Go 构建网站或 Web 服务。
  • JetBrains 的 State of Developer Ecosystem Report 2025 里,Go 开发者常用的选择包括:Gin 48%、net/http 32%、Gorilla 17%、Echo 16%、Chi 12%、Fiber 11%。这道题是多选,所以数字相加会超过 100%。这些数字来自 JetBrains 报告,不是 Go 社区全量普查,但足够看出主流使用习惯。

这些数字里最值得注意的是 Gin 和 net/http 的并存。Gin 已经是使用率最高的第三方框架,标准库仍然占了接近三分之一。

标准库不是退而求其次

很多语言的 Web 开发,默认从框架开始。写 Ruby 很容易想到 Rails,写 Python 很容易想到 Django 或 Flask,写 JavaScript 后端很容易想到 Express、NestJS 或 Fastify。

Go 不太一样,net/http 本身就提供了 HTTP server、request、response、handler、middleware 组合方式和基础路由能力。你完全可以只用标准库写一个能上线的服务。

Go 1.22 以后,标准库的 http.ServeMux 还增强了路由模式,支持方法和通配符。以前很多项目引入路由器,是为了写这种路径参数:

1
router.HandleFunc("/users/{id}", getUser)

现在标准库可以直接写:

1
2
3
4
5
6
7
8
mux := http.NewServeMux()

mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
_ = json.NewEncoder(w).Encode(map[string]string{"id": id})
})

http.ListenAndServe(":8080", mux)

这不是说标准库已经覆盖了所有框架功能。它没有帮你规定项目结构,没有统一的 binding、validation、错误处理和响应格式,也不会自动带一套现成中间件。你往往需要自己写更多胶水代码。

但标准库的优点也很直接:没有第三方依赖,所有 Go 开发者都能读懂,http.Handlerhttp.HandlerFunc 是整个生态里最通用的接口。

很多内部服务、管理后台、简单 API,用 net/http 加少量自写中间件就够了。团队要确认的是,自己能不能接受多写一点样板代码,换取长期的稳定和可迁移性。

Chi 更像路由器,不像大框架

如果你觉得标准库路由够用了,那可以继续用 net/http。如果你想要更舒服的路由分组、中间件挂载、子路由,又想尽量贴近标准库,Chi 是一个很典型的选择。

Chi 的关键点不是功能最多,而是它围绕 net/http 设计。handler 还是这个形状:

1
func(w http.ResponseWriter, r *http.Request)

所以你写出来的中间件通常也是标准库风格:

1
2
3
4
5
func requestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
})
}

已有的 net/http 中间件可以继续用,测试 handler 时也不用理解新的上下文类型。以后从 Chi 迁回标准库,或者从标准库迁到 Chi,改动通常集中在路由注册处。

JetBrains 的数据里,Chi 在 2025 年大约有 12% 的使用率。这个数字没有 Gin 那么醒目,但它解释了 Go Web 生态里一类很稳定的需求:团队想少写路由样板,但不想把业务代码绑到某个框架的上下文对象上。

Gin 解决的是团队默认选项问题

Gin 的 48% 使用率很难忽视。它是 JetBrains 数据里最流行的 Go Web 框架,社区资料多,例子多,面试和项目里也是最常见的。

新人加入项目时,更可能已经见过 Gin 的路由、middleware、gin.Contextc.JSON。遇到 CORS、session、pprof、JWT、限流这类常见需求,也更容易找到现成中间件。

Gin 适合一类团队:不想每个项目都重新讨论 API 怎么组织、错误怎么返回、JSON 怎么写,愿意接受框架约定换开发速度。

Gin 虽然构建在 net/http 之上,但 handler 通常依赖 gin.Context

1
2
3
4
5
r := gin.Default()

r.GET("/users/:id", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"id": c.Param("id")})
})

业务逻辑如果直接读写 gin.Context,后面迁移到其他框架会更费力。比较稳的写法是把框架留在 HTTP 边界,把核心逻辑放到普通 Go 函数里:

1
2
3
4
5
6
7
8
9
10
11
12
13
func getUser(ctx context.Context, id string) (*User, error) {
// 查询数据库、调用下游服务。
return nil, nil
}

r.GET("/users/:id", func(c *gin.Context) {
user, err := getUser(c.Request.Context(), c.Param("id"))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, user)
})

这样写不会让你失去 Gin 的便利,也能避免框架类型一路渗进 service、repository、worker 这些包。

Echo 更重视错误返回这条路径

Echo 和 Gin 很像,都是构建在 net/http 之上的高性能 Web 框架,也都有路由、中间件、binding、rendering 等功能。JetBrains 数据里 Echo 的使用率是 16%。

Echo 一个很明显的差异是 handler 返回 error

1
2
3
4
5
e := echo.New()

e.GET("/users/:id", func(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"id": c.Param("id")})
})

这个设计让集中式错误处理更自然。handler 里返回错误,框架统一处理日志、响应格式和状态码。项目里如果非常重视 API 错误模型,这一点会比少写几行代码更重要。

它的代价和 Gin 类似。Echo 使用自己的 echo.Context,handler 签名也不是标准 http.HandlerFunc。标准库 handler 可以适配进来,但业务代码一旦依赖 Echo 的上下文,迁移成本就会增加。

比较 Gin 和 Echo 时,性能 benchmark 只能当参考。大多数业务 API 的延迟主要花在数据库、缓存、下游 RPC、鉴权和 JSON 编解码上。框架路由差异只有在路径很多、QPS 很高、handler 很轻的服务里才容易成为主要矛盾。

更实际的判断是:团队更熟悉 Gin,还是更喜欢 Echo 这种 handler 返回错误的组织方式。

Fiber 离标准库最远

Fiber 的使用率是 11%,它的定位比较清楚:API 风格接近 Express,强调性能和低内存开销,对 JavaScript 后端背景的开发者比较友好。

它和前面几个框架最大的不同,是底层不是 net/http,而是 fasthttp。这会影响很多事情。标准 http.Handler 不能天然直接用,通用 net/http 中间件也不能直接加载。Fiber 提供 adapter,但 adapter 本身会带来额外转换成本,也会削弱你选择 Fiber 的一部分性能理由。

Fiber 适合团队明确接受这种取舍:想要 Express 风格 API,或者某些服务真的把 HTTP 框架开销放到了很重要的位置,愿意为此放弃一部分标准库兼容性。

如果只是普通 CRUD API、后台管理接口、内部服务,先用 Fiber 多半不是因为性能,而是因为团队偏好。但是要提前考虑到它带来的生态兼容成本。

Gorilla 的数字主要来自存量项目

Gorilla 在 JetBrains 数据里还有 17% 的使用率,高于 Echo、Chi、Fiber 之外的大多数选择。这个数字容易误导新项目。

Gorilla/mux 曾经是 Go 里非常常见的路由器。很多老项目用它,是因为当时标准库路由能力弱,Chi 还没现在这么普遍,Gorilla 提供了路径变量、子路由、方法匹配、host 匹配等实用能力。

后来 Gorilla 项目一度进入归档状态,2023 年又有新的核心维护者接手。再加上 Go 1.22 的 ServeMux 增强、Chi 的成熟,很多新项目会优先考虑标准库或 Chi。

所以看到 17% 时,更合理的理解是:Go Web 项目有大量长期运行的存量代码。一个路由器即使不再是新项目首选,也会因为稳定运行、迁移成本和团队熟悉度继续存在很多年。

如果你的项目已经在用 Gorilla,不需要因为排行榜变化立刻迁移。先考虑两点,一是当前维护状态是否满足安全和兼容需求,二是新需求是否频繁碰到路由、中间件和测试上的限制。

选型时先问三个问题

第一个问题,你需要离 net/http 多近?

如果你希望最大化标准库兼容,优先看 net/http 或 Chi。这样写出来的 handler、中间件、测试工具都比较通用。长期维护时,团队换人和框架迁移的成本也更低。

第二个问题,团队需要多少框架约定?

如果每个服务都在重复写路由分组、日志、恢复、CORS、binding、错误响应,Gin 或 Echo 能减少掉很多项目内自定义约定。框架给出统一写法,对多人协作是有价值的。

第三个问题,性能判断有没有自己的测试?

JetBrains 文章里提到几个框架都有 benchmark,但框架 benchmark 只能说明特定路径、特定 handler、特定压测方式下的结果。真实的测试需要带上现有中间件、JSON 编解码、鉴权、数据库访问和日志,再用 pprof 看 CPU、内存和阻塞点。

如果没有自己的压测数据,就不要把“极致性能”当成主要理由。Go 的标准库、Gin、Echo、Chi 对大多数业务 API 都足够快,线上体验常常取决于超时、连接池、数据库索引、缓存命中率和错误处理。

小结

新项目如果只是几个 API、内部服务、管理接口,可以从 net/http 开始。Go 1.22 以后的路由能力已经能覆盖很多简单需求。

如果路由和中间件开始变多,又希望保留标准库风格,就选 Chi。它给你的主要是路由体验,handler 和 middleware 仍然是 net/http 那套写法。

如果团队已经大量使用 Gin,继续用 Gin 没问题。它的社区、资料和中间件都很成熟。注意把 gin.Context 留在 handler 层,不要让业务包直接依赖它。

如果你喜欢集中式错误处理,或者项目已经围绕 Echo 组织,Echo 是一个合适的选择。它比标准库更有框架感,也比 Fiber 更贴近 net/http

Fiber 要更谨慎一点,它可以很快,也很顺手,尤其对 Express 背景的人。但它的 fasthttp 基础决定了它和 Go 标准 HTTP 生态之间隔了一层。如果在你的业务中性能真的重要,要先用自己的业务路径压测,如果只是喜欢它的 API 风格,也要把后续 adapter、中间件和迁移成本算进去。

Go Web 生态没有一个唯一赢家,这反而是 Go 的特点。标准库足够强,框架才会变成可选项。你要选的,是项目未来几年更愿意承担哪种成本:多写一点标准库样板,还是接受框架上下文、依赖和迁移成本。

参考资料:


欢迎关注我的公众号 iambobbyz 获取最新的文章


Go Web 框架怎么选
https://blog.zhangliangliang.cc/post/go-web-framework.html
作者
Bobby Zhang
发布于
2026年5月5日
许可协议