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) │ │ │ │ │ │ │ │ │ │ │ │ * * * * * *
匹配符號
- 星號(*) :
表示 cron 表達式能匹配該字段的所有值。如在第5個字段使用星號(month),表示每個月
- 斜線(/):
表示增長間隔,如第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
- 逗號(,):
用於枚舉值,如第6個字段值是 MON,WED,FRI,表示 星期一、三、五 執行
- 連字號(-):
表示一個范圍,如第3個字段的值為 9-17 表示 9am 到 5pm 直接每個小時(包括9和17)
- 問號(?):
只用於 日(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主要的設計思路
主要類型或接口說明 (借用大佬)
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{},即空結構體。
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 }
-
Job
:每一個實體包含一個需要運行的Job
這是一個接口,只有一個方法:Run
type Job interface { Run() }
由於 Entity
中需要 Job
類型,因此,我們希望定期運行的任務,就需要實現 Job
接口。同時,由於 Job
接口只有一個無參數無返回值的方法,為了使用方便,作者提供了一個類型:
type FuncJob func()
它通過簡單的實現 Run()
方法來實現 Job 接口:
func (f FuncJob) Run() { f() }
這樣,任何無參數無返回值的函數,通過強制類型轉換為 FuncJob
,就可以當作 Job
來使用了,AddFunc
方法 就是這么做的。
-
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
的兩個具體實現,分別是 SpecSchedule
和 ConstantDelaySchedule
。
① 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 秒執行一次的調度器。
函數調用
- 函數
① 實例化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
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。