Fiber 本身不参与数据库连接,MongoDB 连接逻辑与原生 Go 一致,需在应用启动时用带超时的 context 单次初始化 mongo.Client 并缓存 collection,通过中间件注入 ctx.Locals,禁止在 handler 内重复 Connect 或忽略游标 Close 与上下文超时控制。

直接上结论:Fiber 本身不参与数据库连接,MongoDB 连接逻辑和原生 Go 完全一致,唯一要处理的是把 mongo.Client 安全注入到 Fiber 的请求生命周期中——别用全局变量,也别在每个 handler 里重复 mongo.Connect。
为什么不能在 Fiber handler 里每次调用 mongo.Connect
这会导致连接池爆炸、DNS 频繁解析、context 超时混乱,且 mongo.Connect 是阻塞操作。实测在高并发下会卡住整个路由层,错误日志里常出现 context deadline exceeded 或直接 panic。
- 每次
mongo.Connect都新建一个mongo.Client,而每个 client 默认维护最多 100 个空闲连接;10 个并发 handler 就可能撑爆 MongoDB 的 maxConnections 限制 - Fiber 的 handler 是并发执行的,没加锁的全局 client 指针可能被多个 goroutine 同时写,引发 data race
- 忘记调用
client.Disconnect会导致连接泄漏,进程重启后旧连接仍占着端口或认证 session
怎么把 mongo.Client 正确传进 Fiber handler
推荐用 Fiber 的 Ctx.Locals + 应用启动时单次初始化,不依赖第三方 DI 库也能干净解耦。
- 启动时用带超时的 context 连接 MongoDB,并调用
client.Ping验证连通性,失败直接log.Fatal - 把
*mongo.Client存进 Fiber 的App实例的Settings或用闭包包裹,避免全局变量 - 在中间件里把 client 注入
ctx.Locals("client"),后续 handler 用ctx.Locals("client").(*mongo.Client)取出 - 不要在 handler 内部再做
client.Database(...).Collection(...)链式调用——提前在启动时缓存好*mongo.Collection实例更高效
app := fiber.New()
client, err := mongo.Connect(context.WithTimeout(context.Background(), 10*time.Second), options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
log.Fatal(err)
}
if err = client.Ping(context.TODO(), nil); err != nil {
log.Fatal(err)
}
// 缓存 collection 实例,避免每次查询都重新获取
usersColl := client.Database("myapp").Collection("users")
app.Use(func(c *fiber.Ctx) error {
c.Locals("users_coll", usersColl)
return c.Next()
})
app.Get("/user/:id", func(c *fiber.Ctx) error {
coll := c.Locals("users_coll").(*mongo.Collection)
objID, _ := primitive.ObjectIDHex(c.Params("id"))
var user User
err := coll.FindOne(c.Context(), bson.M{"_id": objID}).Decode(&user)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "not found"})
}
return c.JSON(user)
})
Find 返回空但数据库有数据?重点查这三处
这是 Fiber + MongoDB 组合下最高频的“假失败”,根本不是框架问题,而是 BSON filter 和类型匹配踩坑。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- filter 必须用
bson.M或bson.D,不能用map[string]interface{};哪怕内容一样,driver 会静默忽略 - 字段名大小写严格匹配:DB 里存的是
{"UserName": "alice"},filter 就得写bson.M{"UserName": "alice"},写"username"查不到 - 时间字段必须传
time.Time,不能传字符串(如"2024-01-01")或 int64 时间戳;否则$gt类操作永远不命中 - ObjectId 字段要用
primitive.ObjectIDHex()解析参数,别直接拼字符串;非法 hex 会 panic,不是返回空结果
更新操作没生效、UpdateOne 返回 MatchedCount: 0
说明 filter 根本没匹配到文档,不是 driver 或 Fiber 的锅,90% 是 ObjectId 或时间范围写错了。
- 检查 filter 中的
_id是否用了primitive.ObjectIDHex(c.Params("id"));如果参数是空、长度不对或含非法字符,ObjectIDHex会返回零值ObjectID("000000000000000000000000"),而 DB 里绝不会有这个 ID - 更新语句必须带
$set等操作符:bson.M{"$set": bson.M{"name": "bob"}},直接传bson.M{"name": "bob"}会被当成替换整个文档(且要求所有字段齐全) - 如果集合启用了 schema validation(比如通过 Compass 设置),更新后缺少必填字段或类型不符,会静默失败,
Upsert: false时MatchedCount仍为 0
最易被忽略的一点:Fiber 的 c.Context() 是从 http.Request.Context() 衍生的,它默认没有超时;MongoDB 操作若卡住,整个 HTTP 请求就挂起。务必给每个 FindOne、InsertOne 显式传带超时的 context,比如 context.WithTimeout(c.Context(), 5*time.Second) —— 别依赖 handler 外层的 context。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










