beego/toolbox.NewTask的spec必须为6位cron表达式(秒 分 时 日 月 周),不兼容Linux crontab的5位格式;tk.Run()立即同步执行任务,仅用于测试,调度启动需调用toolbox.StartTask()。

beego/toolbox.NewTask 创建任务时,spec 字段格式必须是 6 位 cron 表达式
beego 的 toolbox.NewTask 不兼容 Linux crontab 的 5 位格式,它强制要求 6 位:秒 分 时 日 月 周(注意顺序和取值范围)。常见错误是直接抄系统 crontab 写法,比如 "0 * * * *",这会 panic 或静默失败。
正确示例:
tk := toolbox.NewTask("check", "0 0 * * * 0", func() error { /* 每天 0 点执行 */ return nil })- 第 1 位是「秒」,取值 0–59;周日是 0,不是 7
- 支持
/(如"0/30 * * * * *"表示每 30 秒)、*、1-5、1,3,5等标准写法 - 若配置来自
AppConfig,务必用beego.AppConfig.String("cron.expr")读取,避免空字符串导致 fallback 到默认值
调用 tk.Run() 会立即执行一次,不是“只定时”
很多开发者误以为 tk.Run() 是启动定时器,其实它是同步触发当前任务函数——无论 cron 表达式怎么写,它都会立刻跑一遍。如果任务耗时长或有副作用(比如发通知、改数据库),这个“预热执行”可能造成重复或状态错乱。
- 真正启用定时调度靠的是
toolbox.StartTask() -
tk.Run()只适合单元测试或调试:验证函数逻辑是否正常,不依赖时间触发 - 生产环境应删掉或注释掉
tk.Run()调用,仅保留toolbox.AddTask()+toolbox.StartTask()
多个任务共存时,AddTask 名称必须唯一,否则后注册的覆盖前一个
toolbox.AddTask("name", tk) 内部用 map 存储,key 是传入的字符串名。如果两次调用都用 "sync",第二次会把第一次注册的任务替换成新的 tk,旧任务永远不会再执行。
- 建议名称带业务前缀,比如
"user.cleanup"、"log.rotate" - 可在初始化阶段加日志确认:
beego.Info("Added task:", "user.cleanup") - 没有内置的「列出所有已注册任务」接口,调试时只能靠日志或自己维护注册记录
StopTask 不会等待正在运行的任务结束,可能中断执行
toolbox.StopTask() 是立即关闭调度器,但不会阻塞等待当前正在运行的 TaskFunc 完成。如果任务里有数据库事务、文件写入或网络请求,中途被 Stop 就可能留下脏数据或连接泄漏。
- 建议在
StopTask()前加超时等待逻辑,例如用context.WithTimeout包裹任务函数主体 - 更稳妥的做法是:应用退出前先禁用新调度(可设 flag 控制),再等几秒让活跃任务自然完成,最后调用
StopTask() - 注意
defer toolbox.StopTask()在 main 函数末尾是不够的,main 结束进程就 kill,goroutine 不保证执行完
beego 的 toolbox 定时能力轻量,但 cron 解析和并发控制都较简单;一旦任务数变多、需要跳过重叠执行、或跨节点去重,就得换用专门的分布式调度器,比如基于 Redis 的 asynq 或自研时间轮。


















