golang cron 定時任務


cron 功能

在Golang中也有這樣一個工具的封裝。提一下cron主要能干什么:

比如我們手機里面設置的鬧鍾,我們可以設置成每天早上7:00,每周周一到周三晚上一點,我們可以把cron形象的看作一個鬧鍾,會在我們規定的時間自動執行一些我們設置好的動作。

作為一個大學生,你可能追求過某某女孩子,你可能有這樣一個需求:每天早上微信給她發早安和晚上給她發晚安。這可能是個不錯的點子。

這都是浮雲,還是努力學習,學會怎么用才是關鍵,不然你也寫不出來。

cron 用法

首先先把第三方庫下載下來:

$ go get -v -u github.com/robfig/cron

我們來看一個小的demo:每隔一秒打印"hello world"

package main import ( "log" "github.com/robfig/cron" ) func main() { log.Println("Starting...") c := cron.New() // 新建一個定時任務對象 c.AddFunc("* * * * * *", func() { log.Println("hello world") }) // 給對象增加定時任務 c.Start() select { } } 

這個小功能用time.Ticker也可以實現,我們為什么要使用cron來完成呢?別急接着往下面看就知道了。

cron表達式

我們在上面demo中使用了AddFunc,第一個參數我們傳遞了一個字符串是:"* * * * * *",這六個*是指什么呢?

 ┌─────────────second 范圍 (0 - 60) │ ┌───────────── min (0 - 59) │ │ ┌────────────── hour (0 - 23) │ │ │ ┌─────────────── day of month (1 - 31) │ │ │ │ ┌──────────────── month (1 - 12) │ │ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to │ │ │ │ │ │ Saturday) │ │ │ │ │ │ │ │ │ │ │ │ * * * * * * 

匹配符號

  1. 星號(*) :表示 cron 表達式能匹配該字段的所有值。如在第5個字段使用星號(month),表示每個月
  2. 斜線(/):表示增長間隔,如第2個字段(minutes) 值是 3-59/15,表示每小時的第3分鍾開始執行一次,之后 每隔 15 分鍾執行一次(即 3(3+0*15)、18(3+1*15)、33(3+2*15)、48(3+3*15) 這些時間點執行),這里也可以表示為:3/15
  3. 逗號(,):用於枚舉值,如第6個字段值是 MON,WED,FRI,表示 星期一、三、五 執行
  4. 連字號(-):表示一個范圍,如第3個字段的值為 9-17 表示 9am 到 5pm 直接每個小時(包括9和17)
  5. 問號(?):只用於 日(Day of month) 和 星期(Day of week),表示不指定值,可以用於代替 *

example

比如我們的手機卡假設都是在每個月的開始時間就更新資費:
"0 0 0 1 * *" // 表示每個月1號的00:00:00
"0 1 1 1 * *" // 表示每個月1號的01:01:00

每隔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 * * ?"

pre-defined schedules

github.com/robfig/cron 包還給我門提供了一些預定義的模式:

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 * * * *
@every <duration> every duration
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") }) 

golang cron主要的設計思路

主要類型或接口說明 (借用大佬)

  1. Cron:包含一系列要執行的實體;支持暫停【stop】;添加實體等
type Cron struct { entries []*Entry stop chan struct{} // 控制 Cron 實例暫停 add chan *Entry // 當 Cron 已經運行了,增加新的 Entity 是通過 add 這個 channel 實現的 snapshot chan []*Entry // 獲取當前所有 entity 的快照 running bool // 當已經運行時為true;否則為false } 

注意,Cron 結構沒有導出任何成員。
注意:有一個成員 stop,類型是 struct{},即空結構體。

  1. Entry:調度實體
type Entry struct { // The schedule on which this job should be run. // 負責調度當前 Entity 中的 Job 執行 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 unsatisfiable // Job 下一次執行的時間 Next 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 Job } 
  1. Job:每一個實體包含一個需要運行的Job
    這是一個接口,只有一個方法:Run
type Job interface { Run() } 

由於 Entity 中需要 Job 類型,因此,我們希望定期運行的任務,就需要實現 Job 接口。同時,由於 Job 接口只有一個無參數無返回值的方法,為了使用方便,作者提供了一個類型:

type FuncJob func()
它通過簡單的實現 Run() 方法來實現 Job 接口:

func (f FuncJob) Run() { f() }
這樣,任何無參數無返回值的函數,通過強制類型轉換為 FuncJob,就可以當作 Job 來使用了,AddFunc 方法 就是這么做的。

  1. Schedule:每個實體包含一個調度器(Schedule)
    負責調度 Job 的執行。它也是一個接口。
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. // 返回同一 Entity 中的 Job 下一次執行的時間 Next(time.Time) time.Time } 

Schedule 的具體實現通過解析 Cron 表達式得到。

庫中提供了 Schedule 的兩個具體實現,分別是 SpecScheduleConstantDelaySchedule

SpecSchedule

type SpecSchedule struct { Second, Minute, Hour, Dom, Month, Dow uint64 } 

從開始介紹的 Cron 表達式可以容易得知各個字段的意思,同時,對各種表達式的解析也會最終得到一個 SpecSchedule 的實例。庫中的 Parse 返回的其實就是 SpecSchedule 的實例(當然也就實現了 Schedule 接口)。

ConstantDelaySchedule

type ConstantDelaySchedule struct { Delay time.Duration // 循環的時間間隔 } 

這是一個簡單的循環調度器,如:每 5 分鍾。注意,最小單位是秒,不能比秒還小,比如 毫秒。

通過 Every 函數可以獲取該類型的實例,如:

constDelaySchedule := Every(5e9)

得到的是一個每 5 秒執行一次的調度器。

函數調用

  1. 函數
    實例化 Cron
func New() *Cron {
    return &Cron{ entries: nil, add: make(chan *Entry), stop: make(chan struct{}), snapshot: make(chan []*Entry), running: false, } } 

可見實例化時,成員使用的基本是默認值;

解析 Cron 表達式

func Parse(spec string) (_ Schedule, err error) 

spec 可以是:

  • Full crontab specs, e.g. “* * * * * ?”
  • Descriptors, e.g. “@midnight”, “@every 1h30m”

② 成員方法

// 將 job 加入 Cron 中 // 如上所述,該方法只是簡單的通過 FuncJob 類型強制轉換 cmd,然后調用 AddJob 方法 func (c *Cron) AddFunc(spec string, cmd func()) error // 將 job 加入 Cron 中 // 通過 Parse 函數解析 cron 表達式 spec 的到調度器實例(Schedule),之后調用 c.Schedule 方法 func (c *Cron) AddJob(spec string, cmd Job) error // 獲取當前 Cron 總所有 Entities 的快照 func (c *Cron) Entries() []*Entry // 通過兩個參數實例化一個 Entity,然后加入當前 Cron 中 // 注意:如果當前 Cron 未運行,則直接將該 entity 加入 Cron 中; // 否則,通過 add 這個成員 channel 將 entity 加入正在運行的 Cron 中 func (c *Cron) Schedule(schedule Schedule, cmd Job) // 新啟動一個 goroutine 運行當前 Cron func (c *Cron) Start() // 通過給 stop 成員發送一個 struct{}{} 來停止當前 Cron,同時將 running 置為 false // 從這里知道,stop 只是通知 Cron 停止,因此往 channel 發一個值即可,而不關心值是多少 // 所以,成員 stop 定義為空 struct func (c *Cron) Stop() 

如果對每個方法調用還是不了解可以去看一下每隔函數的實現源碼

example

package main import ( "log" "github.com/robfig/cron" ) type Hello struct { Str string } func(h Hello) Run() { log.Println(h.Str) } func main() { log.Println("Starting...") c := cron.New() h := Hello{"I Love You!"} // 添加定時任務 c.AddJob("*/2 * * * * * ", h) // 添加定時任務 c.AddFunc("*/5 * * * * * ", func() { log.Println("hello word") }) s, err := cron.Parse("*/3 * * * * *") if err != nil { log.Println("Parse error") } h2 := Hello{"I Hate You!"} c.Schedule(s, h2) // 其中任務 c.Start() // 關閉任務 defer c.Stop() select { } }



作者:_AlphaBaby_
鏈接:https://www.jianshu.com/p/fd3dda663953
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM