Files
oci-portal/.trellis/spec/backend/concurrency.md
T
2026-07-09 19:18:04 +08:00

1.6 KiB

并发规范

Go 后端并发约定。

  • 每个 goroutine 必须有明确退出路径:外部用 context.Context 或 stop channel 通知,内部 select 响应;禁止 fire-and-forget,不允许 goroutine 泄漏。
  • 调用方能等待 goroutine 退出:sync.WaitGroupdone channel。
  • init() 中不启动 goroutine、不做 IO。
  • channel 容量只用 0 或 1,更大缓冲须注释说明理由。
  • 涉及阻塞或远程调用的函数第一个参数是 ctx context.Context,不把 ctx 存进 struct。

模式:服务级后台 goroutine 的落地形态(2026-07 起为既定写法)

面板内「异步旁路」(通知发送 Notifier.SendAsync、日志异步落库 SystemLogService.Record、定期清理 StartCleanup)统一三件套:

s.wg.Add(1)
go func() {
    defer s.wg.Done()
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) // 自带超时,不复用请求 ctx
    defer cancel()
    if err := s.doWork(ctx); err != nil {
        log.Printf("xxx: %v", err) // 旁路失败只记日志,绝不影响主流程
    }
}()
  • 服务暴露 Wait()(内部 wg.Wait());常驻循环(如清理 ticker)额外接收 ctx,select 响应退出。
  • cmd/server 装配的 defer 顺序必须是「先停生产者、再等消费者」(LIFO):defer notifier.Wait() / defer systemLogs.Wait() 写在 defer tasks.Stop() / defer stopCleanup() 之前
  • 陷阱:cron.Stop() 返回的 context 要等(<-s.cron.Stop().Done()),否则仍在执行的任务尚未 wg.AddWait() 会提前返回,goroutine 泄漏且违反 WaitGroup 的 Add/Wait happens-before 约束。