原文地址:Cron定時任務
項目地址:https://github.com/EDDYCJY/go...html
若是對你有所幫助,歡迎點個 Star 或贊 😄git
在實際的應用項目中,定時任務的使用是很常見的。你是否有過 Golang 如何作定時任務的疑問,莫非是輪詢?github
在本文中咱們將結合咱們的項目講述 Crongolang
咱們將使用 cron 這個包,它實現了 cron 規範解析器和任務運行器,簡單來說就是包含了定時任務所需的功能segmentfault
字段名 | 是否必填 | 容許的值 | 容許的特殊字符 |
---|---|---|---|
秒(Seconds) | Yes | 0-59 | * / , - |
分(Minutes) | Yes | 0-59 | * / , - |
時(Hours) | Yes | 0-23 | * / , - |
一個月中的某天(Day of month) | Yes | 1-31 | * / , - ? |
月(Month) | Yes | 1-12 or JAN-DEC | * / , - |
星期幾(Day of week) | Yes | 0-6 or SUN-SAT | * / , - ? |
Cron表達式表示一組時間,使用 6 個空格分隔的字段緩存
能夠留意到 Golang 的 Cron 比 Crontab 多了一個秒級,之後遇到秒級要求的時候就省事了app
一、星號 ( * ) 優化
星號表示將匹配字段的全部值url
二、斜線 ( / )日誌
斜線用戶 描述範圍的增量,表現爲 「N-MAX/x」,first-last/x 的形式,例如 3-59/15 表示此時的第三分鐘和此後的每 15 分鐘,到59分鐘爲止。即從 N 開始,使用增量直到該特定範圍結束。它不會重複
三、逗號 ( , )
逗號用於分隔列表中的項目。例如,在 Day of week 使用「MON,WED,FRI」將意味着星期一,星期三和星期五
四、連字符 ( - )
連字符用於定義範圍。例如,9 - 17 表示從上午 9 點到下午 5 點的每一個小時
五、問號 ( ? )
不指定值,用於代替 「 * 」,相似 「 _ 」 的存在,不難理解
輸入 | 簡述 | 至關於 |
---|---|---|
@yearly (or @annually) | 1月1日午夜運行一次 | 0 0 0 1 1 * |
@monthly | 每月的午夜,每月的第一個月運行一次 | 0 0 0 1 |
@weekly | 每週一次,週日午夜運行一次 | 0 0 0 0 |
@daily (or @midnight) | 天天午夜運行一次 | 0 0 0 * |
@hourly | 每小時運行一次 | 0 0 |
$ go get -u github.com/robfig/cron
在上一章節 Gin實踐 連載十 定製 GORM Callbacks 中,咱們使用了 GORM 的回調實現了軟刪除,同時也引入了另一個問題
就是我怎麼硬刪除,我何時硬刪除?這個每每與業務場景有關係,大體爲
在這裏咱們選用第二種解決方案來進行實踐
打開 models 目錄下的 tag.go、article.go文件,分別添加如下代碼
一、tag.go
func CleanAllTag() bool { db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Tag{}) return true }
二、article.go
func CleanAllArticle() bool { db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Article{}) return true }
注意硬刪除要使用 Unscoped()
,這是 GORM 的約定
在 項目根目錄下新建 cron.go 文件,用於編寫定時任務的代碼,寫入文件內容
package main import ( "time" "log" "github.com/robfig/cron" "github.com/EDDYCJY/go-gin-example/models" ) func main() { log.Println("Starting...") c := cron.New() c.AddFunc("* * * * * *", func() { log.Println("Run models.CleanAllTag...") models.CleanAllTag() }) c.AddFunc("* * * * * *", func() { log.Println("Run models.CleanAllArticle...") models.CleanAllArticle() }) c.Start() t1 := time.NewTimer(time.Second * 10) for { select { case <-t1.C: t1.Reset(time.Second * 10) } } }
在這段程序中,咱們作了以下的事情
一、cron.New()
會根據本地時間建立一個新(空白)的 Cron job runner
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), stop: make(chan struct{}), snapshot: make(chan []*Entry), running: false, ErrorLog: nil, location: location, } }
二、c.AddFunc()
AddFunc 會向 Cron job runner 添加一個 func ,以按給定的時間表運行
func (c *Cron) AddJob(spec string, cmd Job) error { schedule, err := Parse(spec) if err != nil { return err } c.Schedule(schedule, cmd) return nil }
會首先解析時間表,若是填寫有問題會直接 err,無誤則將 func 添加到 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 }
三、c.Start()
在當前執行的程序中啓動 Cron 調度程序。其實這裏的主體是 goroutine + for + select + timer 的調度控制哦
func (c *Cron) Run() { if c.running { return } c.running = true c.run() }
四、time.NewTimer + for + select + t1.Reset
若是你是初學者,大概會有疑問,這是幹嗎用的?
(1)time.NewTimer
會建立一個新的定時器,持續你設定的時間 d 後發送一個 channel 消息
(2)for + select
阻塞 select 等待 channel
(3)t1.Reset
會重置定時器,讓它從新開始計時
(注意,本文適用於 「t.C已經取走,可直接使用 Reset」)
總的來講,這段程序是爲了阻塞主程序而編寫的,但願你帶着疑問來想,有沒有別的辦法呢?
有的,你直接 select{}
也能夠完成這個需求 :)
$ go run cron.go 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 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 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 2018/04/29 17:03:34 Starting... 2018/04/29 17:03:35 Run models.CleanAllArticle... 2018/04/29 17:03:35 Run models.CleanAllTag... 2018/04/29 17:03:36 Run models.CleanAllArticle... 2018/04/29 17:03:36 Run models.CleanAllTag... 2018/04/29 17:03:37 Run models.CleanAllTag... 2018/04/29 17:03:37 Run models.CleanAllArticle...
檢查輸出日誌正常,模擬已軟刪除的數據,定時任務工做OK
定時任務很常見,但願你經過本文可以熟知 Golang 怎麼實現一個簡單的定時任務調度管理
能夠不依賴系統的 Crontab 設置,指不定哪一天就用上了呢