Gin實踐 連載十一 Cron定時任務

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

Cron定時任務

項目地址:https://github.com/EDDYCJY/go...

如果對你有所協助,歡迎點個 Star 或贊

在實際的應用項目中,定時任務的使用是很常見的。你是否有過 Golang 如何做定時任務的疑問,莫非是輪詢?

在本文中我們將結合我們的項目講述 Cron

介紹

我們將使用 cron 這個包,它實現了 cron 規範解析器和任務運行器,簡單來講就是包含了定時任務所需的功能

Cron 運算式格式

欄位名 是否必填 允許的值 允許的特殊字元
秒(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 多了一個秒級,以後遇到秒級要求的時候就省事了

Cron 特殊字元

1、星號 ( * )

星號表示將匹配欄位的所有值

2、斜線 ( / )

斜線使用者 描述範圍的增量,表現為 “N-MAX/x”,first-last/x 的形式,例如 3-59/15 表示此時的第三分鐘和此後的每 15 分鐘,到59分鐘為止。即從 N 開始,使用增量直到該特定範圍結束。它不會重複

3、逗號 ( , )

逗號用於分隔列表中的項目。例如,在 Day of week 使用“MON,WED,FRI”將意味著星期一,星期三和星期五

4、連字號 ( - )

連字號用於定義範圍。例如,9 - 17 表示從上午 9 點到下午 5 點的每個小時

5、問號 ( ? )

不指定值,用於代替 “ * ”,類似 “ _ ” 的存在,不難理解

預定義的 Cron 時間表

輸入 簡述 相當於
@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 的回調實現了虛刪除,同時也引入了另外一個問題

就是我怎麼硬刪除,我什麼時候硬刪除?這個往往與業務情境有關係,大致為

  • 另外有一套硬刪除介面
  • 定時任務清理(或轉移、backup)無效資料

在這裡我們選用第二種解決方案來進行實踐

編寫硬刪除代碼

開啟 models 目錄下的 tag.go、article.go檔案,分別添加以下代碼

1、tag.go

func CleanAllTag() bool {    db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Tag{})    return true}

2、article.go

func CleanAllArticle() bool {    db.Unscoped().Where("deleted_on != ? ", 0).Delete(&Article{})    return true}

注意硬刪除要使用 Unscoped(),這是 GORM 的約定

編寫Cron

在 項目根目錄下建立 cron.go 檔案,用於編寫定時任務的代碼,寫入檔案內容

package mainimport (    "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)        }    }}

在這段程式中,我們做了如下的事情

1、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,    }}

2、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}

3、c.Start()

在當前執行的程式中啟動 Cron 發送器。其實這裡的主體是 goroutine + for + select + timer 的調度控制哦

func (c *Cron) Run() {    if c.running {        return    }    c.running = true    c.run()}

4、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:562018/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:572018/04/29 17:03:34 [info] replacing callback `gorm:delete` from /Users/eddycjy/go/src/github.com/EDDYCJY/go-gin-example/models/models.go:582018/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 設定,指不定哪一天就用上了呢

參考

本系列範例程式碼

  • go-gin-example

本系列目錄

  • Gin實踐 連載一 Golang介紹與環境安裝
  • Gin實踐 連載二 搭建Blog API's(一)
  • Gin實踐 連載三 搭建Blog API's(二)
  • Gin實踐 連載四 搭建Blog API's(三)
  • Gin實踐 連載五 搭建Blog API's(四)
  • Gin實踐 連載六 搭建Blog API's(五)
  • Gin實踐 連載七 Golang優雅重啟HTTP服務
  • Gin實踐 連載八 為它加上Swagger
  • Gin實踐 連載九 將Golang應用部署到Docker
  • Gin實踐 連載十 定製 GORM Callbacks
  • Gin實踐 連載十一 Cron定時任務
  • Gin實踐 番外 Golang交叉編譯
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.