Why
在应用开发中,经常需要一些周期性的操作,如:在每天凌晨分析前一天的日志、每隔5分钟检查某些业务情况并触发告警等等。这些功能需要使用定时任务的方法去实现,那我们是如何使用 Golang 去实现的呢?
What
推荐使用 github 上的 Golang 开源库来实现定时任务,介绍两个常用的库:robfig/cron 和 jasonlvhit/gocron
至于选取哪个?熟悉 crontab 的可以选择 cron,想可读性佳的可以选择 gocron,两者的功能及稳定性都还不错。
如果需要开箱即用的定时任务管理系统(前后端),推荐国人开源的 ouqiang/gocron Star & Fork 数 Σ(o゚д゚oノ)
部门内部目前用的就是这,还是很方便哒~ (づ。◕‿‿◕。)づ 作者 README 写得很清晰,这里就不引申介绍了
How
以下,简单介绍下 robfig/cron 和 jasonlvhit/gocron 的使用
cron
安装 go get -u github.com/robfig/cron
Demo
package mainimport ( "log""github.com/robfig/cron")func main() { i := 0 c := cron.New() spec := "*/5 * * * *" c.AddFunc(spec, func() { i++ log.Println("execute per 5 seconds", i) }) c.Start() select {} }复制代码
执行结果如下(代码说明:每5秒执行一次,i累加并记录打印):
其中,select的用法:golang 的 select 的功能和 select, poll, epoll 相似,就是监听 IO 操作,当 IO 操作发生时,触发相应的动作。
类似的,如果需要执行每分钟执行,其中 spec := "0 */1 * * * *" 改一下即可,了解 crontab 的人应该不陌生其表达格式。总结如下:
字段名 | 是否必须 | 允许的值 | 允许的特定字符 |
---|---|---|---|
秒(Seconds) | 是 | 0-59 | * / , – |
分(Minutes) | 是 | 0-59 | * / , – |
时(Hours) | 是 | 0-23 | * / , – |
日(Day of month) | 是 | 1-31 | * / , – ? |
月(Month) | 是 | 1-12 or JAN-DEC | * / , – |
星期(Day of week) | 是 | 0-6 or SUM-SAT | * / , – ? |
备注:
- 月和星期段的值不区分大小写,如:SUN、Sun和 sun是一样的。
- 星期(Day of week)字段以前的版本貌似非必填,默认* 现在版本就带上吧。
特殊字符说明
- 星号( * ):表示 cron表达式能匹配该字段的所有值。
- 斜线( / ):表示增长间隔;如:spec := "*/5 * * * * *"
- 逗号( , ):用于枚举值,如第6个字段值是 MON,WED,FRI,表示星期一、三、五执行;又例如: spec := "* 0,59 1 * * *",表示每天01:00和 01:59分的每秒都执行一次
- 连字号( - ):表示一个范围,如第3个字段的值为 9-17 表示 9am到 5pm直接每个小时(包括9和17)
- 问号( ? ):只用于日(Day of month)和星期(Day of week),表示不指定值,可以用于代替 *
你也可以使用一些预设的定时器来方便执行,如下:
Entry | Description | Equivalent To |
---|---|---|
@yearly (or @annually) | 每年执行 Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * |
@monthly | 每月执行 Run once a month, midnight, first of month | 0 0 0 1 * * |
@weekly | 每周执行 Run once a week, midnight between Sat/Sun | 0 0 0 * * 0 |
@daily (or @midnight) | 每天执行 Run once a day, midnight | 0 0 0 * * * |
@hourly | 每小时执行 Run once an hour, beginning of hour | 0 0 * * * * |
gocron
安装 go get -u github.com/jasonlvhit/gocron
Demo
package mainimport ( "log""github.com/jasonlvhit/gocron")func main() { i := 0 s := gocron.NewScheduler() s.Every(5).Seconds().Do(func() { i++ log.Println("execute per 5 seconds", i) }) <-s.Start() }复制代码
执行效果如下:
以上为基础定时器使用方式,gocron接口的命名及使用相对人性化,基本可直读,参考以下官方示例:
package mainimport ( "fmt""github.com/jasonlvhit/gocron")func task() { fmt.Println("I am runnning task.") }func taskWithParams(a int, b string) { fmt.Println(a, b) }func main() { // Do jobs with params gocron.Every(1).Second().Do(taskWithParams, 1, "hello") // Do jobs safely, preventing an unexpected panic from bubbling up gocron.Every(1).Second().DoSafely(taskWithParams, 1, "hello") // Do jobs without params gocron.Every(1).Second().Do(task) gocron.Every(2).Seconds().Do(task) gocron.Every(1).Minute().Do(task) gocron.Every(2).Minutes().Do(task) gocron.Every(1).Hour().Do(task) gocron.Every(2).Hours().Do(task) gocron.Every(1).Day().Do(task) gocron.Every(2).Days().Do(task) // Do jobs on specific weekday gocron.Every(1).Monday().Do(task) gocron.Every(1).Thursday().Do(task) // function At() take a string like 'hour:min' gocron.Every(1).Day().At("10:30").Do(task) gocron.Every(1).Monday().At("18:30").Do(task) // remove, clear and next_run _, time := gocron.NextRun() fmt.Println(time) gocron.Remove(task) gocron.Clear() // function Start start all the pending jobs <- gocron.Start() // also, you can create a new scheduler// to run two schedulers concurrently s := gocron.NewScheduler() s.Every(3).Seconds().Do(task) <- s.Start() }复制代码