原文地址:Cron定时任务
项目地址:https://github.com/EDDYCJY/go...html
若是对你有所帮助,欢迎点个 Star 或赞 😄git
在实际的应用项目中,定时任务的使用是很常见的。你是否有过 Golang 如何作定时任务的疑问,莫非是轮询?github
在本文中咱们将结合咱们的项目讲述 Crongolang
咱们将使用 cron 这个包,它实现了 cron 规范解析器和任务运行器,简单来说就是包含了定时任务所需的功能segmentfault
字段名 | 是否必填 | 容许的值 | 容许的特殊字符 |
---|---|---|---|
秒(Seconds) | Yes | 0-59 | * / , - |
分(Minutes) | Yes | 0-59 | * / , - |
时(Hours) | Yes | 0-23 | * / , - |
一个月中的某天(Day of month) | Yes | 1-31 | * / , - ? |
月(Month) | Yes | 1-12 or JAN-DEC | * / , - |
星期几(Day of week) | Yes | 0-6 or SUN-SAT | * / , - ? |
Cron表达式表示一组时间,使用 6 个空格分隔的字段缓存
能够留意到 Golang 的 Cron 比 Crontab 多了一个秒级,之后遇到秒级要求的时候就省事了app
一、星号 ( * ) 优化
星号表示将匹配字段的全部值url
二、斜线 ( / )日志
斜线用户 描述范围的增量,表现为 “N-MAX/x”,first-last/x 的形式,例如 3-59/15 表示此时的第三分钟和此后的每 15 分钟,到59分钟为止。即从 N 开始,使用增量直到该特定范围结束。它不会重复
三、逗号 ( , )
逗号用于分隔列表中的项目。例如,在 Day of week 使用“MON,WED,FRI”将意味着星期一,星期三和星期五
四、连字符 ( - )
连字符用于定义范围。例如,9 - 17 表示从上午 9 点到下午 5 点的每一个小时
五、问号 ( ? )
不指定值,用于代替 “ * ”,相似 “ _ ” 的存在,不难理解
输入 | 简述 | 至关于 |
---|---|---|
@yearly (or @annually) | 1月1日午夜运行一次 | 0 0 0 1 1 * |
@monthly | 每月的午夜,每月的第一个月运行一次 | 0 0 0 1 |
@weekly | 每周一次,周日午夜运行一次 | 0 0 0 0 |
@daily (or @midnight) | 天天午夜运行一次 | 0 0 0 * |
@hourly | 每小时运行一次 | 0 0 |
$ go get -u github.com/robfig/cron
在上一章节 Gin实践 连载十 定制 GORM Callbacks 中,咱们使用了 GORM 的回调实现了软删除,同时也引入了另一个问题
就是我怎么硬删除,我何时硬删除?这个每每与业务场景有关系,大体为
在这里咱们选用第二种解决方案来进行实践
打开 models 目录下的 tag.go、article.go文件,分别添加如下代码
一、tag.go
func CleanAllTag() bool { db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Tag{}) return true }
二、article.go
func CleanAllArticle() bool { db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Article{}) return true }
注意硬删除要使用 Unscoped()
,这是 GORM 的约定
在 项目根目录下新建 cron.go 文件,用于编写定时任务的代码,写入文件内容
package main import ( "time" "log" "github.com/robfig/cron" "github.com/EDDYCJY/go-gin-example/models" ) func main() { log.Println("Starting...") c := cron.New() c.AddFunc("* * * * * *", func() { log.Println("Run models.CleanAllTag...") models.CleanAllTag() }) c.AddFunc("* * * * * *", func() { log.Println("Run models.CleanAllArticle...") models.CleanAllArticle() }) c.Start() t1 := time.NewTimer(time.Second * 10) for { select { case <-t1.C: t1.Reset(time.Second * 10) } } }
在这段程序中,咱们作了以下的事情
一、cron.New()
会根据本地时间建立一个新(空白)的 Cron job runner
func New() *Cron { return NewWithLocation(time.Now().Location()) } // NewWithLocation returns a new Cron job runner. func NewWithLocation(location *time.Location) *Cron { return &Cron{ entries: nil, add: make(chan *Entry), stop: make(chan struct{}), snapshot: make(chan []*Entry), running: false, ErrorLog: nil, location: location, } }
二、c.AddFunc()
AddFunc 会向 Cron job runner 添加一个 func ,以按给定的时间表运行
func (c *Cron) AddJob(spec string, cmd Job) error { schedule, err := Parse(spec) if err != nil { return err } c.Schedule(schedule, cmd) return nil }
会首先解析时间表,若是填写有问题会直接 err,无误则将 func 添加到 Schedule 队列中等待执行
func (c *Cron) Schedule(schedule Schedule, cmd Job) { entry := &Entry{ Schedule: schedule, Job: cmd, } if !c.running { c.entries = append(c.entries, entry) return } c.add <- entry }
三、c.Start()
在当前执行的程序中启动 Cron 调度程序。其实这里的主体是 goroutine + for + select + timer 的调度控制哦
func (c *Cron) Run() { if c.running { return } c.running = true c.run() }
四、time.NewTimer + for + select + t1.Reset
若是你是初学者,大概会有疑问,这是干吗用的?
(1)time.NewTimer
会建立一个新的定时器,持续你设定的时间 d 后发送一个 channel 消息
(2)for + select
阻塞 select 等待 channel
(3)t1.Reset
会重置定时器,让它从新开始计时
(注意,本文适用于 “t.C已经取走,可直接使用 Reset”)
总的来讲,这段程序是为了阻塞主程序而编写的,但愿你带着疑问来想,有没有别的办法呢?
有的,你直接 select{}
也能够完成这个需求 :)
$ go run cron.go 2018/04/29 17:03:34 [info] replacing callback `gorm:update_time_stamp` from /Users/eddycjy/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:56 2018/04/29 17:03:34 [info] replacing callback `gorm:update_time_stamp` from /Users/eddycjy/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:57 2018/04/29 17:03:34 [info] replacing callback `gorm:delete` from /Users/eddycjy/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:58 2018/04/29 17:03:34 Starting... 2018/04/29 17:03:35 Run models.CleanAllArticle... 2018/04/29 17:03:35 Run models.CleanAllTag... 2018/04/29 17:03:36 Run models.CleanAllArticle... 2018/04/29 17:03:36 Run models.CleanAllTag... 2018/04/29 17:03:37 Run models.CleanAllTag... 2018/04/29 17:03:37 Run models.CleanAllArticle...
检查输出日志正常,模拟已软删除的数据,定时任务工做OK
定时任务很常见,但愿你经过本文可以熟知 Golang 怎么实现一个简单的定时任务调度管理
能够不依赖系统的 Crontab 设置,指不定哪一天就用上了呢