推荐后端HTTP层优先选net/http,轻量可控;模型加载须用sync.Once全局初始化;特征传输用protobuf或gob替代JSON;排序过滤用sort.SliceStable保障稳定性。

推荐服务用 net/http 还是 gin?别硬套框架
Go 写推荐后端,HTTP 层选型不是“越新越好”。net/http 足够轻量、可控,尤其当推荐逻辑本身不复杂(比如基于规则+热度+用户画像 ID 查表),加一层 gin 反而多出中间件调度开销和 panic 恢复成本。但如果你需要统一鉴权、日志埋点、OpenAPI 文档生成,gin 的 Use 和 @router 注解确实省事。
关键判断点:curl -X POST http://localhost:8080/recommend?uid=123 这种简单请求是否占 80% 以上?如果是,直接用 http.HandleFunc + json.NewDecoder 解析参数更稳。
- 避免在
gin.Context中反复调用c.Param或c.Query,提前提取到结构体里再传给推荐函数 - 不要用
gin.Default()上线——它的Logger和Recovery默认写 stdout,压测时容易卡死 - 若用
net/http,记得设http.Server.ReadTimeout和WriteTimeout,防慢连接拖垮整个服务
recommend.go 里不该出现模型加载逻辑
AI 推荐后端最常犯的错:把 torch.Load 或 onnx.NewSession 放在 HTTP handler 里。每次请求都初始化一次模型,内存暴涨、延迟飙升。Go 的 init 阶段或 sync.Once 才是正解。
真实场景中,模型权重文件(如 model.onnx)通常放在本地磁盘或挂载的 NFS 上。启动时一次性加载进内存,后续所有 goroutine 共享同一个 session 实例。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
var once sync.Once+var model *onnx.Session全局变量封装加载逻辑 - 加载失败必须 panic(而不是 log.Fatal),否则服务看似启动成功,实际一调用就
nil pointer dereference - 如果模型支持热更新(比如定期拉新权重),别用
os.Stat轮询——改用fsnotify监听文件变更,触发once.Do替换 session
用户特征向量怎么喂给模型?别用 JSON 直传原始 float64 切片
HTTP body 传 {"features": [0.23, 0.88, ...]} 看似直观,实则危险:JSON 解码 float64 有精度损失,且切片长度不固定时易触发 GC 压力。生产环境更推荐二进制协议。
两个务实选择:protocol buffers(适合多语言协作)或 gob(纯 Go 场景下序列化效率更高)。前者定义 .proto 文件声明 repeated float32 features,后者直接 encode []float32,无类型擦除。
- 用
gob时,务必在 encode/decode 前调用gob.Register([]float32(nil)),否则解码失败静默返回零值 - 如果前端是 JS,无法用 gob,那就用 protobuf +
base64编码二进制 payload,比 JSON 小 40% 以上 - 永远校验特征维度:模型输入是 128 维,但请求只传了 127 个数?立刻返回
400 Bad Request,别让 ONNX Runtime 报 obscure error
推荐结果排序后还要做业务过滤?用 sort.SliceStable 而非 sort.Slice
AI 模型输出的是 raw score,但线上必须叠加业务规则:比如屏蔽已读内容、降权竞品广告、按频道分桶限流。这些规则往往依赖原始请求上下文(如用户所在城市、客户端版本),不能全塞进模型里重训。
常见错误是先 sort.Slice 再遍历过滤,结果排序不稳定——相同 score 的 item 因 goroutine 调度顺序不同,每次返回顺序不一致,导致 AB 测试难归因。
- 用
sort.SliceStable保证相等 score 下维持原始索引顺序(即模型输出顺序) - 过滤逻辑写成独立函数,接收
[]Item和context.Context(用于传入设备 ID、时间戳等运行时信息) - 别在过滤里做远程调用(如查 Redis 是否已读)——预加载到内存 map,用
map[string]bool快速判断
真正麻烦的从来不是模型推理那几十毫秒,而是特征拼接时一次没 cache 住的 MySQL 查询,或是过滤环节漏掉的时区转换 bug。

















