Go 每日一库之定时任务库:cron

以下文章来源于Golang技术分享 , 作者机器铃砍菜刀
在Linux中 , Cron是计划任务管理系统 , 通过crontab命令使任务在约定的时间执行已经计划好的工作 , 例如定时备份系统数据、周期性清理缓存、定时重启服务等 。 本文介绍的cron库 , 就是用Go实现Linux中crontab命令的相似效果 。
使用示例
安装下载cron , 目前最新的稳定版已经迭代到了v3
gogetgithub.com/robfig/cron/v3@v3.0.0在项目中导入包
import"github.com/robfig/cron/v3"使用
1packagemain23import(4"fmt"56"github.com/robfig/cron/v3"7)89funcmain(){10c:=cron.New()11c.AddFunc("30****",func(){fmt.Println("Everyhouronthehalfhour")})12c.AddFunc("303-6,20-23***",func(){fmt.Println("..intherange3-6am,8-11pm")})13c.AddFunc("CRON_TZ=Asia/Tokyo3004***",func(){fmt.Println("Runsat04:30Tokyotimeeveryday")})14c.AddFunc("@hourly",func(){fmt.Println("Everyhour,startinganhourfromnow")})15c.AddFunc("@every1h30m",func(){fmt.Println("Everyhourthirty,startinganhourthirtyfromnow")})16c.AddFunc("@every1s",func(){fmt.Println("Every1second,startinganhourthirtyfromnow")})17c.Start()18select{}19}创建cron对象
使用时 , 首先通过cron.New()创建cron对象 , 通过该对象管理定时任务 。 通过调用cron的AddFunc()方法添加定时任务 。 AddFun()入参为二 , 参数一是以字符串的形式指定触发任务规则 , 参数二是无入参的函数 , 任务触发时执行函数 。
添加触发任务30****表示每个小时内的第30分钟时触发;303-6,20-23***表示在早上3点到6点 , 下午8点到11点的第30分钟时触发;CRON_TZ=Asia/Tokyo3004***表示东京时间每天早上4点半触发;@hourly表示从添加该任务时算起的之后每小时触发;@every1h30m表示从添加该任务时算起的之后每一个半小时触发;@every1s表示从添加该任务时算起的之后每秒触发 。
启动定时循环通过调用cron.Start()启动定时循环任务 。
输出
1$gorunmain.go2Every1second,startinganhourthirtyfromnow3Every1second,startinganhourthirtyfromnow4Every1second,startinganhourthirtyfromnow5Every1second,startinganhourthirtyfromnow6...由于只让以上程序运行了几秒的时间 , 因此 , 输出中只包含执行了每秒触发的打印 。 随着程序运行时间的加长 , 其他触发任务也会在满足条件时进行打印 。
cron时间表达式规则
cron表达式默认通过使用5个以空格分隔的字段组合来表示触发时间(和linux的crontab保持一致) 。
1cron.New(2cron.WithParser(3cron.NewParser(4cron.Second|cron.Minute|cron.Hour|cron.Dom|cron.Month|cron.Dow|cron.Descriptor)))如同30****一样 , 默认第1个字段表示分钟 , 第2个字段表示小时 , 第3个字段表示每月中的日期 , 第4个字段表示月份数 , 第5个字段表示星期几 。
cron还提供了强大的自定义时间格式功能 , 可以通过调用cron.NewParser()创建自定义Parser对象 , 例如通过以下方式定义新的cron时间表达式规则
1cron.New(cron.WithSeconds())这样 , 时间字段一共是7位 , 第1位就是指定秒 。 秒执行的时间表达式就可以用1******表示 。
因为添加Seconds是对标准cron规范的最常见修改 , 因此cron提供了一个内置函数cron.WithSeconds()来执行此操作 , 该函数等效于之前使用的自定义解析器 。
1cron.New(cron.WithSeconds())预定义时间表由于cron的时间表达式可读性不是很好 , 因此cron库预定义了一些字符串来表示特定的时间规则 。
1Entry|Description|EquivalentTo2-----|-----------|-------------3@yearly(or@annually)|Runonceayear,midnight,Jan.1st|0011*4@monthly|Runonceamonth,midnight,firstofmonth|001**5@weekly|Runonceaweek,midnightbetweenSat/Sun|00**06@daily(or@midnight)|Runonceaday,midnight|00***7@hourly|Runonceanhour,beginningofhour|0****时间间隔cron还提供了更具可读性的固定时间间隔格式
1@every它代码每隔duration触发执行一次任务 。 这里的duration是通过调用标准库time的ParseDuration()函数解析的 , 所以只要ParseDuration()支持的格式都能支持 。 例如上文示例的@every1h30m和@every1s 。
cron可控选项
在cron源码option.go文件中 , 暴露了5个函数供开发者控制cron对象的选项 。
WithLocation()指定时区 。 默认情况下基于当前时区(在Unix系统中 , 查询TZ环境变量确定要使用的时区 , 若未定义TZ , 则使用/etc/localtime文件中的定义时区) 。 可通过在时间字符串前添加CRON_TZ=字符串再加上具体的时区 。 例如东京时区为Asia/Tokyo 。
1c.AddFunc("CRON_TZ=Asia/Tokyo3004***",func(){fmt.Println("Runsat04:30Tokyotimeeveryday")})WithParser()自定义时间解析器 , 上文已有示例 , 这里不再赘述 。
WithSeconds()增加对秒的时间格式支持 , 其内部调用的WithParser()方法 。
1funcWithSeconds()Option{2returnWithParser(NewParser(3Second|Minute|Hour|Dom|Month|Dow|Descriptor,4))5}WithChain()Job包装器 , 下文中会讲解Job接口 。
WithLogger()Logger是cron中用于记录日志的接口 , WithLogger()可以设置自定义的Logger 。
1packagemain23import(4"fmt"5"log"6"os"78"github.com/robfig/cron/v3"9)1011funcmain(){12c:=cron.New(cron.WithLogger(cron.VerbosePrintfLogger(log.New(os.Stdout,"cronprocess:",log.LstdFlags))))13c.AddFunc("@every1s",func(){fmt.Println("Every1second")})14c.Start()15select{}16}该logger记录了cron内部的调度过程 , 输入如下
1gorunmain.go2cronprocess:2020/08/3000:07:04start3cronprocess:2020/08/3000:07:04schedule,now=2020-08-30T00:07:04+08:00,entry=1,next=2020-08-30T00:07:05+08:004cronprocess:2020/08/3000:07:05wake,now=2020-08-30T00:07:05+08:005cronprocess:2020/08/3000:07:05run,now=2020-08-30T00:07:05+08:00,entry=1,next=2020-08-30T00:07:06+08:006Every1second7cronprocess:2020/08/3000:07:06wake,now=2020-08-30T00:07:06+08:008Every1second9cronprocess:2020/08/3000:07:06run,now=2020-08-30T00:07:06+08:00,entry=1,next=2020-08-30T00:07:07+08:0010cronprocess:2020/08/3000:07:07wake,now=2020-08-30T00:07:07+08:0011cronprocess:2020/08/3000:07:07run,now=2020-08-30T00:07:07+08:00,entry=1,next=2020-08-30T00:07:08+08:0012Every1second13...自定义Job
cron中定义了Job接口 , 对象只要实现了Job接口所定义的Run()方法 , 均可以调用cron.AddJob()方法将该对象添加到定时管理器中 。
1//Jobisaninterfaceforsubmittedcronjobs.2typeJobinterface{3Run()4}AddFunc()
在上文示例中 , 通过cron.AddFunc()方法为cron对象添加定时任务 。 实质上 , AddFunc()方法内部调用的也是AddJob()方法:定义新类型对象FuncJob , 为其实现Job接口 , 在AddFunc()方法中 , 将回调参数func()转为FuncJob类型 , 调用AddJob()方法 。
1typeFuncJobfunc()23func(fFuncJob)Run(){f()}45func(c*Cron)AddFunc(specstring,cmdfunc())(EntryID,error){6returnc.AddJob(spec,FuncJob(cmd))7}自实现Job接口除了通过AddFunc()将无参函数直接作为回调外 , 我们还可以通过AddJon()自定义对象 。
如下 , 自定义对象CallJob , 实现Run()方法 。
1packagemain23import(4"fmt"5"time"67"github.com/robfig/cron/v3"8)910typeCallJobstruct{11namestring12numberint13}1415func(cCallJob)Run(){16fmt.Printf("call%s:%dn",c.name,c.number)17}18funcmain(){19c:=cron.New()20c.AddJob("@every1s",CallJob{21name:"Bob",22number:13888888888,23})24c.Start()2526time.Sleep(3*time.Second)27}输出
1$gorunmain.go2callBob:138888888883callBob:138888888884callBob:13888888888总结
cron库为go开发者提供了强大的定时任务管理功能 , 它的时间表达式格式和linux下的crontab命令是对齐的 。
cron的代码并不算多 , 其核心定时管理功能依赖了go标准库time和sort , 非常值得学习和参考 。 另外有一个基于该库抽离出来的最小化定时任务库gron , 更易理解和使用 , 文末会附上该库地址 。
cron是小菜刀在实际项目中引入过的三方库 , 感觉挺不错 , 就总结出来分享给大家 。 如果你喜欢看更多关于三方库的文章 , 请点赞支持 。
仓库地址
1.【Go 每日一库之定时任务库:cron】2.


    推荐阅读