news 2026/5/8 7:44:59

go语言定时任务工具类

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
go语言定时任务工具类

go语言定时任务工具类,支持crontab(精确到秒)和 timer 两种模式。
本文介绍了一个基于Go语言的定时任务工具类,支持crontab(精确到秒)和timer两种模式。工具类使用github.com/robfig/cron/v3包实现,主要功能包括:

提供NamedCronJobTask接口定义定时任务,支持通过cron表达式或时间间隔两种调度方式
实现了任务添加(AddTask)、更新(UpdateTask)等功能
内部使用map管理任务,支持同名任务替换
对cron和ticker两种调度方式进行了封装,自动处理panic恢复
提供了任务日志记录功能

该工具类设计灵活,可以方便地集成到各种需要定时任务调度的Go应用中。

引入包:github.com/robfig/cron/v3我的版本 v3.0.1

直接见代码:
1、cron_job_conf.go

packagejobimport("xxx/utils"// 自定义工具包,包内容见下"fmt""log""sync""time""github.com/google/uuid""github.com/robfig/cron/v3")// 定时任务接口typeNamedCronJobTaskinterface{// 任务名称Name()string// returns a cron expression and/or a time interval.// Scheduling priority:// - If cronSpec is non-empty, it is used (interval is ignored).// - Else, if interval > 0, a ticker-based scheduler runs every 'interval'.// - Otherwise, the task is invalid.// Example:// return "*/5 * * * * *", 0 // cron every 5 seconds// return "", 2*time.Hour // ticker every 2 hours// return "0 0 * * *", 10*time.Minute // cron used (every day at 00:00)SpecOrInterval()(cronSpecstring,interval time.Duration)// 执行方法Execute()}// taskType 任务类型typetaskTypeintconst(taskTypeCron taskType=iotataskTypeTicker)// cronTask 内部任务元数据typecronTaskstruct{specstring// cron 表达式 或 "[TICKER:...]" 占位符(仅用于日志)fnfunc()id cron.EntryID// cron 用ticker*time.Ticker// ticker 用stopChanchanstruct{}// 用于安全停止 ticker goroutinetaskType taskType}var(c*cron.Cron cronTaskMap=make(map[string]*cronTask)cronTaskMapMutex=&sync.RWMutex{})// init 初始化 cron 调度器(带秒支持)funcinit(){c=cron.New(cron.WithSeconds())c.Start()log.Println("cron scheduler started")}// AddTask 添加定时任务。如果之前已存在同名任务则会被覆盖。funcAddTask(named NamedCronJobTask)(string,error){ifnamed==nil{return"",fmt.Errorf("namedCronJobTask is nil")}name:=named.Name()ifname==""{name="anonymous:"+uuid.New().String()}returnAddTaskWithName(name,named.SpecOrInterval,named.Execute)}// AddTaskWithName 添加命名任务,如果之前已存在同名任务则会被覆盖。// specOrInterval 返回 (cronSpec, interval),规则:// - 若 cronSpec != "" → 使用 cron(忽略 interval)// - 否则若 interval > 0 → 使用 ticker// - 否则返回错误funcAddTaskWithName(namestring,specOrIntervalfunc()(specstring,interval time.Duration),fnfunc(),)(string,error){iffn==nil{returnname,fmt.Errorf("task function cannot be nil")}ifspecOrInterval==nil{returnname,fmt.Errorf("specOrInterval function cannot be nil")}cronSpec,interval:=specOrInterval()ifname==""{name="anonymous:"+uuid.New().String()}ifcronSpec==""&&interval<=0{returnname,fmt.Errorf("invalid scheduling policy for task %q: must return non-empty cronSpec or interval > 0",name,)}wrappedFn:=func(){deferutils.RecoverPanic()fn()}cronTaskMapMutex.Lock()defercronTaskMapMutex.Unlock()// 替换已存在的同名任务ifold,ok:=cronTaskMap[name];ok{removeTask(old)log.Printf("Replaced existing task: %s",name)}ifcronSpec!=""{// 使用 cronentryID,err:=c.AddFunc(cronSpec,wrappedFn)iferr!=nil{returnname,fmt.Errorf("invalid cron spec %q: %w",cronSpec,err)}cronTaskMap[name]=&cronTask{spec:cronSpec,fn:fn,id:entryID,taskType:taskTypeCron,}log.Printf("Added cron task: %s (spec: %s, ID: %d)",name,cronSpec,int64(entryID))}elseifinterval>0{// 使用 tickerstopChan:=make(chanstruct{})ticker:=time.NewTicker(interval)gofunc(){deferfunc(){ifr:=recover();r!=nil{log.Printf("Recovered panic in ticker task %s: %v",name,r)}ticker.Stop()}()for{select{case<-ticker.C:wrappedFn()case<-stopChan:return}}}()cronTaskMap[name]=&cronTask{spec:fmt.Sprintf("[TICKER:%v]",interval),fn:fn,ticker:ticker,stopChan:stopChan,taskType:taskTypeTicker,}log.Printf("Added ticker task: %s (every %v)",name,interval)}returnname,nil}// AddTaskWithName 添加匿名任务,通过函数动态获取调度策略。// specOrInterval 返回 (cronSpec, interval),规则:// - 若 cronSpec != "" → 使用 cron(忽略 interval)// - 否则若 interval > 0 → 使用 ticker// - 否则返回错误funcAddTaskWithoutName(specOrIntervalfunc()(specstring,interval time.Duration),fnfunc())(string,error){name:="anonymous:"+uuid.New().String()returnAddTaskWithName(name,specOrInterval,fn)}// UpdateTask 更新任务调度策略,支持在 cron 和 ticker 之间切换。// newSpecOrInterval 应返回新的 (cronSpec, interval)。// 规则:// - 若 cronSpec != "" → 使用 cron(忽略 interval)// - 否则若 interval > 0 → 使用 ticker// - 否则返回错误//// 要求任务必须已存在。funcUpdateTask(namestring,newSpecOrIntervalfunc()(newSpecstring,newInterval time.Duration))error{ifname==""{returnfmt.Errorf("task name cannot be empty")}ifnewSpecOrInterval==nil{returnfmt.Errorf("newSpecOrInterval function cannot be nil")}newSpec,newInterval:=newSpecOrInterval()ifnewSpec==""&&newInterval<=0{returnfmt.Errorf("newSpecOrInterval returned invalid spec (%q) and interval (%v): at least one must be valid",newSpec,newInterval)}cronTaskMapMutex.Lock()defercronTaskMapMutex.Unlock()oldTask,exists:=cronTaskMap[name]if!exists{returnfmt.Errorf("task not found: %s",name)}ifoldTask.fn==nil{returnfmt.Errorf("task function is nil for %s",name)}// 安全停止旧任务removeTask(oldTask)ifnewSpec!=""{// 切换为 cronwrappedFn:=func(){deferutils.RecoverPanic()oldTask.fn()}entryID,e:=c.AddFunc(newSpec,wrappedFn)ife!=nil{delete(cronTaskMap,name)// 防止残留无效条目returnfmt.Errorf("failed to parse new cron spec %q: %w",newSpec,e)}cronTaskMap[name]=&cronTask{spec:newSpec,fn:oldTask.fn,id:entryID,taskType:taskTypeCron,}log.Printf("Updated task %s to cron (spec: %s)",name,newSpec)}elseifnewInterval>0{// 切换为 tickerstopChan:=make(chanstruct{})ticker:=time.NewTicker(newInterval)gofunc(){deferfunc(){ifr:=recover();r!=nil{log.Printf("Recovered panic in updated ticker task %s: %v",name,r)}ticker.Stop()}()for{select{case<-ticker.C:func(){deferutils.RecoverPanic()oldTask.fn()}()case<-stopChan:return}}}()cronTaskMap[name]=&cronTask{spec:fmt.Sprintf("[TICKER:%v]",newInterval),fn:oldTask.fn,ticker:ticker,stopChan:stopChan,taskType:taskTypeTicker,}log.Printf("Updated task %s to ticker (interval: %v)",name,newInterval)}returnnil}// RemoveTaskByName 删除任务(幂等:任务不存在也返回 true)funcRemoveTaskByName(namestring)bool{ifname==""{returntrue}cronTaskMapMutex.Lock()defercronTaskMapMutex.Unlock()iftask,ok:=cronTaskMap[name];ok{removeTask(task)delete(cronTaskMap,name)log.Printf("Removed task: %s",name)}returntrue}// Exists 检查任务是否存在funcExists(namestring)bool{cronTaskMapMutex.RLock()defercronTaskMapMutex.RUnlock()_,ok:=cronTaskMap[name]returnok}// GetAllTaskNames 获取所有任务名funcGetAllTaskNames()[]string{cronTaskMapMutex.RLock()defercronTaskMapMutex.RUnlock()names:=make([]string,0,len(cronTaskMap))forname:=rangecronTaskMap{names=append(names,name)}returnnames}// Stop 停止整个调度器funcStop(){log.Println("Stopping cron scheduler...")c.Stop()// 停止 cron 调度器(不再触发新任务)cronTaskMapMutex.Lock()defercronTaskMapMutex.Unlock()// 复用 removeTask 清理所有任务资源for_,task:=rangecronTaskMap{removeTask(task)}log.Println("All scheduled tasks stopped")}// (内部使用)安全移除一个任务funcremoveTask(task*cronTask){switchtask.taskType{casetaskTypeCron:c.Remove(task.id)casetaskTypeTicker:iftask.ticker!=nil{task.ticker.Stop()}iftask.stopChan!=nil{close(task.stopChan)// 唯一关闭点task.stopChan=nil// 防止重复 close(虽已加锁,但更安全)}}}

2、panic_tookit.go

packageutilsimport("log""runtime/debug")// 捕获 PanicfuncRecoverPanic(){ifr:=recover();r!=nil{log.Printf("panic recovered: %v\n%s",r,debug.Stack())// 后期这里还可以 SendMetrics、SendAlert、Sentry.Capture等指标}}// 在捕获 Panic 下运行某个方法funcRunWithRecoverPanic[T any](data T,fnfunc(T)){deferRecoverPanic()// 增加一层防火墙fn(data)}// 在捕获 Panic 下运行某个方法(无入参)funcRunWithRecoverPanic2(fnfunc()){deferRecoverPanic()// 增加一层防火墙fn()}// 在捕获 Panic 下 异步运行(goroutine) 某个方法funcAsyncRunWithRecoverPanic[T any](data T,fnfunc(T)){gofunc(d T){deferRecoverPanic()// 增加一层防火墙fn(d)}(data)}// 在捕获 Panic 下 异步运行(goroutine) 某个方法(无入参)funcAsyncRunWithRecoverPanic2(fnfunc()){gofunc(){deferRecoverPanic()// 增加一层防火墙fn()}()}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/7 18:53:26

为什么WebPShop能让你的设计效率提升300%?

WebPShop是一款专为Adobe Photoshop设计的开源插件&#xff0c;它彻底解决了设计师在处理现代WebP格式图像时的各种痛点。作为支持WebP静态图像和动画的专业工具&#xff0c;它让设计师能够在熟悉的Photoshop环境中无缝处理这一高效图像格式。 【免费下载链接】WebPShop Photos…

作者头像 李华
网站建设 2026/5/1 17:28:57

从零搭建量子开发环境,VSCode插件集成全解析

第一章&#xff1a;从零认识量子开发与VSCode集成量子计算作为前沿科技领域&#xff0c;正逐步从理论走向实践。随着开发者对量子算法和量子程序的兴趣日益增长&#xff0c;如何搭建高效的开发环境成为入门的第一步。Visual Studio Code&#xff08;简称 VSCode&#xff09;凭借…

作者头像 李华
网站建设 2026/5/1 15:44:20

终极键盘可视化指南:让每个操作都清晰可见

终极键盘可视化指南&#xff1a;让每个操作都清晰可见 【免费下载链接】keycastr KeyCastr, an open-source keystroke visualizer 项目地址: https://gitcode.com/gh_mirrors/ke/keycastr 在数字时代&#xff0c;键盘操作已成为我们日常工作和学习的核心环节。无论是录…

作者头像 李华
网站建设 2026/5/6 19:54:15

揭秘量子计算镜像构建难题:如何一键生成精准技术文档

第一章&#xff1a;量子计算镜像的文档生成在构建量子计算模拟环境时&#xff0c;自动化文档生成是确保系统可维护性和可扩展性的关键环节。通过集成代码注释与运行时元数据&#xff0c;可以动态生成反映当前量子态、门操作序列和电路结构的完整技术文档。文档生成流程 扫描量子…

作者头像 李华
网站建设 2026/5/4 17:21:14

昆明靠谱的餐饮运营究竟哪家强?

“今天吃什么&#xff1f;”这一问题困扰着无数人&#xff0c;也从侧面反映出餐饮行业竞争的激烈。在这样的市场环境下&#xff0c;餐饮运营显得至关重要&#xff0c;那么靠谱的餐饮运营究竟该怎么做呢&#xff1f; 准确定位与市场分析 餐饮运营的第一步是明确自身定位。要深入…

作者头像 李华
网站建设 2026/5/3 5:57:06

GP2040-CE完全指南:打造专业级游戏控制器的终极教程

GP2040-CE是一款专为Raspberry Pi Pico设计的开源游戏控制器固件&#xff0c;为DIY爱好者和游戏玩家提供了前所未有的定制自由。无论你是想打造专属的格斗摇杆&#xff0c;还是需要适配多平台的游戏控制器&#xff0c;这个项目都能满足你的需求。 【免费下载链接】GP2040-CE …

作者头像 李华