Cron定时任务

    如果对你有所帮助,欢迎点个 Star 或赞 ?

    在实际的应用项目中,定时任务的使用是很常见的。你是否有过 Golang 如何做定时任务的疑问,莫非是轮询?

    在本文中我们将结合我们的项目讲述 Cron

    我们将使用 这个包,它实现了 cron 规范解析器和任务运行器,简单来讲就是包含了定时任务所需的功能

    Cron表达式表示一组时间,使用 6 个空格分隔的字段

    可以留意到 Golang 的 Cron 比 Crontab 多了一个秒级,以后遇到秒级要求的时候就省事了

    Cron 特殊字符

    1、星号 ( * )

    星号表示将匹配字段的所有值

    2、斜线 ( / )

    斜线用户 描述范围的增量,表现为 “N-MAX/x”,first-last/x 的形式,例如 3-59/15 表示此时的第三分钟和此后的每 15 分钟,到59分钟为止。即从 N 开始,使用增量直到该特定范围结束。它不会重复

    3、逗号 ( , )

    逗号用于分隔列表中的项目。例如,在 Day of week 使用“MON,WED,FRI”将意味着星期一,星期三和星期五

    4、连字符 ( - )

    连字符用于定义范围。例如,9 - 17 表示从上午 9 点到下午 5 点的每个小时

    不指定值,用于代替 “ * ”,类似 “ _ ” 的存在,不难理解

    输入 简述 相当于
    @yearly (or ) 1月1日午夜运行一次 0 0 0 1 1 *
    @monthly 每个月的午夜,每个月的第一个月运行一次 0 0 0 1
    每周一次,周日午夜运行一次 0 0 0 0
    @daily (or ) 每天午夜运行一次 0 0 0 *
    @hourly 每小时运行一次 0 0

    安装

    在上一章节 中,我们使用了 GORM 的回调实现了软删除,同时也引入了另外一个问题

    就是我怎么硬删除,我什么时候硬删除?这个往往与业务场景有关系,大致为

    • 另外有一套硬删除接口
    • 定时任务清理(或转移、backup)无效数据

    在这里我们选用第二种解决方案来进行实践

    编写硬删除代码

    打开 models 目录下的 tag.go、article.go文件,分别添加以下代码

    1、tag.go

    1. db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Tag{})
    2. return true
    3. }

    2、article.go

    1. func CleanAllArticle() bool {
    2. db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Article{})
    3. return true
    4. }

    注意硬删除要使用 Unscoped(),这是 GORM 的约定

    在 项目根目录下新建 cron.go 文件,用于编写定时任务的代码,写入文件内容

    在这段程序中,我们做了如下的事情

    1、cron.New()

    会根据本地时间创建一个新(空白)的 Cron job runner

    1. func New() *Cron {
    2. return NewWithLocation(time.Now().Location())
    3. }
    4. // NewWithLocation returns a new Cron job runner.
    5. func NewWithLocation(location *time.Location) *Cron {
    6. return &Cron{
    7. add: make(chan *Entry),
    8. stop: make(chan struct{}),
    9. snapshot: make(chan []*Entry),
    10. running: false,
    11. ErrorLog: nil,
    12. location: location,
    13. }
    14. }

    2、c.AddFunc()

    AddFunc 会向 Cron job runner 添加一个 func ,以按给定的时间表运行

    1. func (c *Cron) AddJob(spec string, cmd Job) error {
    2. schedule, err := Parse(spec)
    3. if err != nil {
    4. }
    5. c.Schedule(schedule, cmd)
    6. return nil
    7. }

    3、c.Start()

    在当前执行的程序中启动 Cron 调度程序。其实这里的主体是 goroutine + for + select + timer 的调度控制哦

    1. func (c *Cron) Run() {
    2. return
    3. }
    4. c.running = true
    5. c.run()
    6. }

    4、time.NewTimer + for + select + t1.Reset

    如果你是初学者,大概会有疑问,这是干嘛用的?

    (1)time.NewTimer

    会创建一个新的定时器,持续你设定的时间 d 后发送一个 channel 消息

    (2)for + select

    阻塞 select 等待 channel

    (3)t1.Reset

    会重置定时器,让它重新开始计时
    (注意,本文适用于 “t.C已经取走,可直接使用 Reset”)


    总的来说,这段程序是为了阻塞主程序而编写的,希望你带着疑问来想,有没有别的办法呢?

    有的,你直接 select{} 也可以完成这个需求 :)

    验证

    1. $ go run cron.go
    2. 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
    3. 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
    4. 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
    5. 2018/04/29 17:03:34 Starting...
    6. 2018/04/29 17:03:35 Run models.CleanAllArticle...
    7. 2018/04/29 17:03:35 Run models.CleanAllTag...
    8. 2018/04/29 17:03:36 Run models.CleanAllArticle...
    9. 2018/04/29 17:03:36 Run models.CleanAllTag...
    10. 2018/04/29 17:03:37 Run models.CleanAllTag...

    检查输出日志正常,模拟已软删除的数据,定时任务工作OK

    定时任务很常见,希望你通过本文能够熟知 Golang 怎么实现一个简单的定时任务调度管理

    参考

    本系列示例代码