Go定时器cron的使用详解

2020-01-28 12:54:41丽君

6)L,W,#

Go中没有L,W,#的用法,下文作解释。

cron举例说明

每隔5秒执行一次:*/5 * * * * ?

每隔1分钟执行一次:0 */1 * * * ?

每天23点执行一次:0 0 23 * * ?

每天凌晨1点执行一次:0 0 1 * * ?

每月1号凌晨1点执行一次:0 0 1 1 * ?

在26分、29分、33分执行一次:0 26,29,33 * * * ?

每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?

下载安装

控制台输入 go get github.com/robfig/cron 去下载定时任务的Go包,前提是你的 $GOPATH 已经配置好

源码解析

文件目录讲解 


constantdelay.go   #一个最简单的秒级别定时系统。与cron无关
constantdelay_test.go #测试
cron.go        #Cron系统。管理一系列的cron定时任务(Schedule Job)
cron_test.go     #测试
doc.go        #说明文档
LICENSE        #授权书 
parser.go       #解析器,解析cron格式字符串城一个具体的定时器(Schedule)
parser_test.go    #测试
README.md       #README
spec.go        #单个定时器(Schedule)结构体。如何计算自己的下一次触发时间
spec_test.go     #测试

cron.go

结构体:


// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running. 
// Cron保持任意数量的条目的轨道,调用相关的func时间表指定。它可以被启动,停止和条目,可运行的同时进行检查。
type Cron struct {
  entries []*Entry      // 任务
  stop   chan struct{}   // 叫停止的途径
  add   chan *Entry    // 添加新任务的方式
  snapshot chan []*Entry   // 请求获取任务快照的方式
  running bool        // 是否在运行
  ErrorLog *log.Logger    // 出错日志(新增属性)
  location *time.Location   // 所在地区(新增属性)    
}

// Entry consists of a schedule and the func to execute on that schedule.
// 入口包括时间表和可在时间表上执行的func
type Entry struct {
    // 计时器
  Schedule Schedule
  // 下次执行时间
  Next time.Time
  // 上次执行时间
  Prev time.Time
  // 任务
  Job Job
}

关键方法:


// 开始任务
// Start the cron scheduler in its own go-routine, or no-op if already started.
func (c *Cron) Start() {
  if c.running {
    return
  }
  c.running = true
  go c.run()
}
// 结束任务
// Stop stops the cron scheduler if it is running; otherwise it does nothing.
func (c *Cron) Stop() {
  if !c.running {
    return
  }
  c.stop <- struct{}{}
  c.running = false
}

// 执行定时任务
// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
  // Figure out the next activation times for each entry.
  now := time.Now().In(c.location)
  for _, entry := range c.entries {
    entry.Next = entry.Schedule.Next(now)
  }
    // 无限循环
  for {
      //通过对下一个执行时间进行排序,判断那些任务是下一次被执行的,防在队列的前面.sort是用来做排序的
    sort.Sort(byTime(c.entries))

    var effective time.Time
    if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
      // If there are no entries yet, just sleep - it still handles new entries
      // and stop requests.
      effective = now.AddDate(10, 0, 0)
    } else {
      effective = c.entries[0].Next
    }

    timer := time.NewTimer(effective.Sub(now))
    select {
    case now = <-timer.C: // 执行当前任务
      now = now.In(c.location)
      // Run every entry whose next time was this effective time.
      for _, e := range c.entries {
        if e.Next != effective {
          break
        }
        go c.runWithRecovery(e.Job)
        e.Prev = e.Next
        e.Next = e.Schedule.Next(now)
      }
      continue

    case newEntry := <-c.add: // 添加新的任务
      c.entries = append(c.entries, newEntry)
      newEntry.Next = newEntry.Schedule.Next(time.Now().In(c.location))

    case <-c.snapshot: // 获取快照
      c.snapshot <- c.entrySnapshot()

    case <-c.stop:  // 停止任务
      timer.Stop()
      return
    }

    // 'now' should be updated after newEntry and snapshot cases.
    now = time.Now().In(c.location)
    timer.Stop()
  }
}