golang的cron實現

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

cron定時計劃任務,golang的cron開源項目程式碼分析:https://github.com/robfig/cron

package cronimport ("log""runtime""sort""time")// 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.type Cron struct {entries  []*Entry //Entry是所有任務封裝的結構stop     chan struct{} //關閉flag的channeladd      chan *Entry   //新增的Entrysnapshot chan []*Entry //Entry的備份running  bool //開啟flagErrorLog *log.Loggerlocation *time.Location}// Job is an interface for submitted cron jobs.type Job interface {Run() //Job只有一個Run方法}// The Schedule describes a job's duty cycle.type Schedule interface {// Return the next activation time, later than the given time.// Next is invoked initially, and then each time the job is run.Next(time.Time) time.Time //調度選取下一次執行的時候,整個流程執行非常關鍵不住}// Entry consists of a schedule and the func to execute on that schedule.type Entry struct {// The schedule on which this job should be run.Schedule Schedule// The next time the job will run. This is the zero time if Cron has not been// started or this entry's schedule is unsatisfiableNext time.Time //下一次啟動並執行時間// The last time this job was run. This is the zero time if the job has never// been run.Prev time.Time //上一次啟動並執行時間// The Job to run.Job Job}// byTime is a wrapper for sorting the entry array by time// (with zero time at the end).type byTime []*Entryfunc (s byTime) Len() int      { return len(s) }func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }func (s byTime) Less(i, j int) bool {// 比較時間,兩個都是0的時候直接返回false,0比任何時間優先順序高if s[i].Next.IsZero() {return false}if s[j].Next.IsZero() {return true}return s[i].Next.Before(s[j].Next)}// New returns a new Cron job runner, in the Local time zone.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), //新增entry的channelstop:     make(chan struct{}),snapshot: make(chan []*Entry),running:  false,ErrorLog: nil,location: location, //時間}}// A wrapper that turns a func() into a cron.Jobtype FuncJob func()func (f FuncJob) Run() { f() }// AddFunc adds a func to the Cron to be run on the given schedule.func (c *Cron) AddFunc(spec string, cmd func()) error {return c.AddJob(spec, FuncJob(cmd))}// AddJob adds a Job to the Cron to be run on the given schedule.func (c *Cron) AddJob(spec string, cmd Job) error {schedule, err := Parse(spec)if err != nil {return err}c.Schedule(schedule, cmd)return nil}// Schedule adds a Job to the Cron to be run on the given 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}//當前啟動並執行所有Entry的一份拷貝,方便測試和跟蹤func (c *Cron) Entries() []*Entry {if c.running {//先傳一個nil,觸發channel的讀時間,run的協程複製entry,然後讀取出所有的entryc.snapshot <- nilx := <-c.snapshotreturn x}return c.entrySnapshot()}// Location gets the time zone locationfunc (c *Cron) Location() *time.Location {return c.location}//Start方法和Run方法的區別就是在不在單獨的go-routine裡面運行// 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 = truego c.run()}// Run the cron scheduler, or no-op if already running.func (c *Cron) Run() {if c.running {return}//開始cron的運行c.running = truec.run()}func (c *Cron) runWithRecovery(j Job) {defer func() {//運行job panic的時候日誌記錄棧資訊if r := recover(); r != nil {const size = 64 << 10buf := make([]byte, size)buf = buf[:runtime.Stack(buf, false)]c.logf("cron: panic running job: %v\n%s", r, buf)}}()j.Run()}// 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 := c.now()for _, entry := range c.entries {entry.Next = entry.Schedule.Next(now)}for {// 從小到大的排序entrysort.Sort(byTime(c.entries))var timer *time.Timerif len(c.entries) == 0 || c.entries[0].Next.IsZero() {// 沒有entry的時候設定超大的timer,依然可以處理stoptimer = time.NewTimer(100000 * time.Hour)} else {timer = time.NewTimer(c.entries[0].Next.Sub(now))}for {select {case now = <-timer.C:now = now.In(c.location)// 運行比now時間小的entryfor _, e := range c.entries {if e.Next.After(now) || e.Next.IsZero() {break}//執行任務Jobgo c.runWithRecovery(e.Job)e.Prev = e.Nexte.Next = e.Schedule.Next(now)}case newEntry := <-c.add://新增entry時候,停掉上一次的timer,timer.Stop()now = c.now()newEntry.Next = newEntry.Schedule.Next(now)c.entries = append(c.entries, newEntry)case <-c.snapshot:c.snapshot <- c.entrySnapshot()continuecase <-c.stop:timer.Stop()return}break}}}// Logs an error to stderr or to the configured error logfunc (c *Cron) logf(format string, args ...interface{}) {if c.ErrorLog != nil {c.ErrorLog.Printf(format, args...)} else {log.Printf(format, args...)}}// 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}// entrySnapshot returns a copy of the current cron entry list.func (c *Cron) entrySnapshot() []*Entry {entries := []*Entry{}for _, e := range c.entries {entries = append(entries, &Entry{Schedule: e.Schedule,Next:     e.Next,Prev:     e.Prev,Job:      e.Job,})}return entries}// now returns current time in c locationfunc (c *Cron) now() time.Time {return time.Now().In(c.location)}

其實關鍵就是Schedule的Next的方法的具體實現

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.