Go 中的框架问题

Go 与网络框架的关系很奇怪。标准库的 net/http 真的很好。你可以使用纯标准库构建生产 API,而不会觉得在和工具斗争。

但大多数团队最终会使用路由库。问题是选哪个。

讨论最多的三个:ChiFiberGin。每个都有不同的理念。吸引不同类型的开发者。

真正的答案:它比你想象的更重要

在深入比较之前,这里有一个令人不安的事实:对于大多数 API,在 Chi、Fiber 和 Gin 之间选择不会对你的应用性能、稳定性或可维护性产生有意义的影响。

路由器是 net/http 之上的薄层。这些库之间的原始吞吐量差异在基准测试中可测量,在生产中无关紧要。你的数据库比你的路由器慢。你的查询设计比 JSON 序列化速度更重要。

这并不意味着选择没有意义。这意味着你应该出于正确的原因做出选择:API 设计、中间件人体工程学和团队熟悉度 — 而不是基准数字。

Chi:极简且与 stdlib 对齐

Chi 将自己定位为一个贴近 net/http 的轻量级路由库。理念:如果你懂 net/http,你就懂 Chi。

r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)

r.Route("/api/v1", func(r chi.Router) {
    r.Get("/users", listUsers)
    r.Post("/users", createUser)
    r.Get("/users/{id}", getUser)
})

路由语法清晰。Route 模式将相关处理器干净地分组。中间件链读起来很自然。

Chi 没有内置的 JSON 绑定层。你自带 — 大多数人将其与 go-json 或标准的 encoding/json 配对。这既是优点(你选择依赖项)也是缺点(你必须选择依赖项),取决于你的偏好。

最适合: 想要路由清晰度而不要框架魔法的团队。net/http 兼容性很重要的项目。希望准确了解请求路径中发生什么的 API。

Fiber:Go 对 Express.js 的回答

Fiber 受 Express.js 启发。如果你来自 Node.js,Fiber 会立即感觉熟悉。

app := fiber.New()
app.Get("/users", func(c *fiber.Ctx) error {
    return c.JSON(users)
})

API 简洁。错误处理明确。中间件系统内置。

Fiber 在底层使用 fasthttp — 一种以兼容性换取速度的替代高性能 HTTP 包。如果你需要最后的每秒请求数,这很重要。对于大多数 API,不是。

最适合: 来自 Node.js/Express 的团队。性能关键 API,其中 fasthttp 权衡值得。快速开发,你想要一站式服务。

Gin:久经考验的默认选择

Gin 多年来一直是最受欢迎的 Go 路由库。社区庞大、文档广泛、中间件生态系统成熟。

r := gin.Default()
r.GET("/users", func(c *gin.Context) {
    var users []User
    db.Find(&users)
    c.JSON(200, users)
})

Gin 的路由很快。通过 gin.Context 的错误处理对于大多数常见情况都很符合人体工程学。绑定系统(c.ShouldBindJSON)处理样板。

Gin 做对的最重要的事情:当出现问题时,错误消息是清晰的。恢复中间件是可靠的。日志开箱即用很有用。

最适合: 大多数生产 Go API。想要有社区支持的框架的团队。你需要在 Stack Overflow 上找到答案的项目。

中间件问题

所有三个路由库都有中间件系统。都可以包装处理器、修改上下文和链接操作。差异在细节中。

Chi 中间件:

r.Use(middleware.RequestID)
r.Use(middleware.RealIP)

Fiber 中间件:

app.Use(favicon.New())
app.Use(compress.New())

Gin 中间件:

r.Use(gin.Logger())
r.Use(gin.Recovery())

它们都能工作。它们都做你需要的一切。按对你的团队有意义的语法选择,然后继续。

诚实的性能比较

这是你会在基准测试中看到的数字:

路由库 请求/秒(约)
Fiber (fasthttp) ~600K
Gin ~280K
Chi ~250K

这些数字来自合成基准测试,处理器中没有任何实际工作发生。在生产中,你的 API 正在做数据库查询、外部 API 调用、JSON 序列化。路由器开销是噪音。

按人体工程学选择。按你的团队知道的东西选择。性能差异永远不会成为你的瓶颈。

什么时候 stdlib 真的够了

这里有一个被低估的选项:直接使用 net/http 和一个小路由助手。

mux := http.NewServeMux()
mux.HandleFunc("GET /users/", listUsers)
mux.HandleFunc("POST /users/", createUser)

Go 1.22 为标准库的 ServeMux 添加了增强的路由模式。模式 GET /users/POST /users/ 现在被正确解析。你可以获得基于方法的路由,而无需外部依赖。

如果你的 API 很简单 — CRUD 端点、没有复杂的中间件链、不需要绑定 — stdlib 可能是正确的选择。更简单的依赖。没有框架需要升级。只是 Go。

决策框架

  1. 小团队,快速迭代 → Fiber 一站式服务、类似 Express 的 API、快速开发。

  2. 有 Go 经验的团队,生产 API → Gin 久经考验、庞大的社区、良好的文档。

  3. 极简偏好,与 stdlib 对齐 → Chi 轻量、明确、不碍事。

  4. 简单 API,少量端点 → stdlib net/http 无依赖。只是 Go。

真正重要的事情:无论你选择什么,有意识地选择。不要仅仅因为它很受欢迎就默认选择 Gin,而不考慮它是否适合你的项目。


下一篇:Go 项目的长期可维护性结构 — 两年迭代后真正有效的布局