beego 中禁止在 controller 中裸调用 go 启动 goroutine,因其脱离请求上下文、无法取消、易致内存泄漏和资源失效;应使用 context.context 与 sync.waitgroup 统一管理生命周期。

Beego 中直接用 go 启动 goroutine 很危险
Beego 本身不管理 goroutine 生命周期,go sendEmail(...) 这类裸调用一旦出错或主进程退出,任务就静默丢失。更糟的是,如果在 Controller 方法里启动 goroutine,它会脱离 HTTP 请求上下文,但又没绑定任何 cancel 信号 —— 既无法被中断,也无法被等待,还可能持有 request 或 response 的引用导致内存泄漏。
- 别在
Controller.Get()或Post()里写go func() { ... }() - 不要依赖
time.Sleep等待 goroutine 结束;主线程退出后它们还在跑,但日志、DB 连接、HTTP client 可能已关闭 - 若任务需访问 Beego 的
beego.AppConfig或orm,必须确保初始化已完成,否则可能 panic
用 context.Context + sync.WaitGroup 控制后台任务生命周期
Beego 没有内置任务调度器,你得自己搭骨架。核心是让每个后台 goroutine 接收一个带取消能力的 ctx,并在主程序退出前统一 cancel。
- 在
beego.AppInit()阶段创建context.WithCancel(context.Background()),保存到全局变量或 app config - 启动 goroutine 时传入该
ctx,并在循环中监听select { case - 用
sync.WaitGroup计数:启动前wg.Add(1),退出前wg.Done() - 在
beego.BeeApp.Shutdown钩子中调用cancel(),再wg.Wait()等待自然退出
示例关键片段:
var (
taskCtx context.Context
taskCancel context.CancelFunc
wg sync.WaitGroup
)
<p>func initTask() {
taskCtx, taskCancel = context.WithCancel(context.Background())
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case </p><p>func shutdownTask() {
if taskCancel != nil {
taskCancel()
wg.Wait()
}
}
</p>
Beego 定时任务(beego.Cron)不是万能解法
beego.Cron 适合秒级/分钟级简单轮询,比如刷新缓存、清理临时文件。但它底层是基于 time.Ticker 的单 goroutine 循环,不支持并发执行、无失败重试、不记录运行状态,也不受 context 控制。
- 避免用
Cron.AddFunc("@every 10s", fn)做耗时操作(如发邮件、调第三方 API),会阻塞整个 ticker 循环 - 若需并发,得自己包装:在 cron 回调里往 worker pool channel 发任务,而非直接执行
-
Cron.Start()必须在beego.Run()之前调用,否则不生效 - 它不感知 Beego 的热重启或配置更新,改了 cron 表达式要重启服务
真正需要可靠后台任务时,别硬扛 —— 上消息队列
Beego 对接 RabbitMQ 或 Redis Queue 是生产环境更稳的选择。裸用 goroutine 只能解决“不卡住 HTTP 响应”这个表层问题,但扛不住崩溃、重试、堆积、监控等真实需求。
- RabbitMQ 消费者必须作为独立长任务启动(在
AppInit),不能绑在 Controller 里;ACK 必须由该 goroutine 自己完成 - 用
amqp091-go连接时,务必加重试和超时:amqp.Dial(amqpURL + "?connect_timeout=5&heartbeat=30") - 每条消息处理开头打日志时带上
msg.MessageId和msg.DeliveryTag,否则多消费者并行时日志根本对不上 - 若只是中小规模、无高可用要求,SQLite 做本地任务表比 Redis 更可控:用
UPDATE ... WHERE status = 'pending' RETURNING *抢占任务,天然防重复
最易被忽略的一点:所有后台任务的错误日志,必须包含可追踪的唯一标识(如请求 ID、消息 ID、任务 ID)。Beego 默认日志不带 trace 字段,不补上这个,出问题时连哪条任务挂了都找不到。











