mongo-go-driver连接必须带超时context,禁用context.todo();需用context.withtimeout包裹connect和ping,连接池显式配置,结构体字段必加bson tag,游标必须close。

mongo-go-driver 连接必须带超时 context,不能用 context.TODO()
直接写 mongo.Connect(context.TODO(), options.Client().ApplyURI(uri)) 是最常见也最危险的错误——它会让连接卡在 DNS 解析、网络不可达或防火墙拦截时无限等待,最终导致服务启动失败或 handler 阻塞。生产环境必须显式控制超时。
实操建议:
- 用
context.WithTimeout包裹 Connect 和后续Ping:比如ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) -
Ping必须传新 context(不能复用 Connect 时那个),否则超时逻辑不生效 - URI 务必从环境变量读取:
os.Getenv("MONGODB_URI"),避免硬编码密码或 localhost - 连接池参数要显式设:
options.Client().SetMaxPoolSize(20).SetMinPoolSize(5)
结构体字段必须用 bson tag,不是 json tag
Go 结构体映射 BSON 文档完全依赖 bson tag,漏写、拼错或类型不匹配,FindOne 就返回空值但不报错——这是最隐蔽的 bug 来源,调试时根本看不出哪错了。
关键点:
- ID 字段必须写成
ID primitive.ObjectID `bson:"_id,omitempty"`,下划线不能少,“omitempty”影响插入是否生成新_id - 时间字段类型必须是
time.Time,tag 写成bson:"created_at",不能是字符串或int64 - 嵌套结构体每一层都要加
bsontag,否则子字段不会被序列化/反序列化 - 手动构造查询条件(如
bson.M{"age": 25})时,value 类型必须和数据库字段一致,"age": "25"会查不到
Find() 游标必须 Close,且 defer 要绑定当前请求 context
Find() 返回的 *mongo.Cursor 底层持有网络连接和缓冲区,不 Close 就会泄漏 goroutine 和内存,高并发下很快 OOM。
正确姿势:
- 必须写
defer cursor.Close(ctx),这里的ctx是 handler 入参的请求生命周期 context,不是全局 context - 遍历游标必须先
cursor.Next(),再cursor.Decode();跳过Next直接Decode会 panic - 批量结果可用
cursor.All()转切片,但要注意内存占用;流式处理更安全,适合大结果集
GORM 连接 PostgreSQL 时别忽略 DSN 安全与迁移控制
用 GORM 连 PostgreSQL,dsn 硬编码在代码里(比如 "host=localhost user=postgres password=postgres dbname=fiber_demo...")等于把密码明文提交进 Git,CI/CD 流水线一跑就泄露。
该做的几件事:
- DSN 从环境变量组装:
os.Getenv("DB_HOST")+os.Getenv("DB_USER")等,或统一用url.Parse(os.Getenv("DATABASE_URL")) - 初始化后立即执行
db.AutoMigrate(&User{}),但仅限开发;生产环境应使用独立 migration 工具(如 golang-migrate) - 别在 handler 里反复调用
gorm.Open—— Client 必须全局复用,否则连接池失控 - 如果用
db.WithContext(ctx),注意ctx必须带 timeout,因为 Fiber 的c.Context()不具备标准库 context 的取消能力











