認識一下 Golang 定時框架
cron,相信玩過 Linux 的朋友應該都對這個很熟悉吧,這不就是計劃任務嘛,當你需要某個時間段去執行某一件事的時候,普通的執行方式已經不能滿足我們的需求了,這個時候就需要一個定時任務了,那麼下面我就來介紹一個 Go 的開源定時任務框架。
一、安裝
# 下載
go get github.com/robfig/cron/v3@v3.0.0
# 導入:這裏後面加個v3意味着這是v3版本,注意不要搞錯
import "github.com/robfig/cron/v3"
新建一個 Go 項目,直接在項目中執行命令下載依賴,這裏下載的是官網的最新版本,我們介紹也是採用的 v3 版本,注意這裏不能搞錯了,v3 版本跟之前的版本表達式寫法還是有一點點不同的,此處筆者就曾踩坑,其實就是自己沒有看清楚版本就開始用,導致在這個地方翻車了。並且官網給出的說明中突出體現了由於使用 Go 模塊,它需要 Go 1.11 或更高版本的環境,如果你的環境不滿足這個要求的話,那你就無法使用這個,建議使用之前的版本。不過推薦還是使用 v3 版本,這個版本相較之前的版本做了不少的改動,還是很值得推薦的。具體的話我這裏就不過多描述了,文檔中寫的很詳細,可以看下 v3 的文檔 https://pkg.go.dev/github.com/robfig/cron/v3#section-readme。
二、用法
c := cron.New()
c.AddFunc("30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("30 3-6,20-23 * * *", func() { fmt.Println(".. in the range 3-6am, 8-11pm") })
c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * *", func() { fmt.Println("Runs at 04:30 Tokyo time every day") })
c.AddFunc("@hourly", func() { fmt.Println("Every hour, starting an hour from now") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty, starting an hour thirty from now") })
c.Start()
..
// Funcs are invoked in their own goroutine, asynchronously.
...
// Funcs may also be added to a running Cron
c.AddFunc("@daily", func() { fmt.Println("Every day") })
..
// Inspect the cron job entries' next and previous run times.
inspect(c.Entries())
..
c.Stop() // Stop the scheduler (does not stop any jobs already running).
這是官網給出的例子,如果你使用 v3 的話,不妨可以直接試試看,但你注意,如果你是直接用的之前的 cron 表達式的話,那麼在這裏執行的時候就會報錯,因爲 v3 跟之前以及其他 cron 框架不同的一點在於它的 cron 表達式只有 5 個參數,它默認的是從分開始解析的,而不是秒,這個如果不看文檔直接執行的話,那麼就會有一個報錯拋給你,這一點需要注意。cron 表達式表示一組時間,使用 5 個空格分隔的字段,這是每個字段具體的含義,跟之前的就少了個秒而已,其實無傷大雅。
Field name | Mandatory? | Allowed values | Allowed special characters
---------- | ---------- | -------------- | --------------------------
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 | * / , - ?
那麼這個時候有的小夥伴可能就會問了,v3 版本的從分開始,那我之前的都是從秒開始,並且我現在還是有精確到秒的定時任務需要處理,那我還可以用這個框架嘛?答案是可以的,人家官方就知道這種肯定一時間無法全部摒棄秒級表達式,就告訴你可以使用自定義解析器來實現它。
cron.New(
cron.WithParser(
cron.NewParser(
cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)))
這東西乍一看是不是感覺很懵,不要着急,這個看不懂也無妨,官方知道由於添加秒數是對標準 cron 規範最常見的修改,因此提供了一個內置函數來執行此操作,這相當於上面看到的自定義解析器,只是它的秒數字段是必需的。
cron.New(cron.WithSeconds())
這個時候 cron 表達式就是 6 個字段了,不能再少秒了,否則也會報錯。特殊字符的用法跟其他的 cron 表達式是一致的,這個沒有區別,這裏簡單介紹一下。
Asterisk (*)
- 表示 cron 表達式將匹配該字段的所有值;例如,在第 5 個字段(月份)中使用星號將表示每個月。
Slash (/)
/ 用於描述範圍的增量。例如,第一個字段(分鐘)中的 3-59/15 表示該小時的第 3 分鐘以及此後每 15 分鐘一次。形式 “*/...” 相當於形式 “first-last/...”,即在字段的最大可能範圍內遞增。“N/...” 形式被接受爲“N-MAX/...”,即從 N 開始,使用增量直到該特定範圍的末尾。它不會環繞。
Comma (,)
, 用於分隔列表中的項目。例如,在第 5 個字段(星期幾)中使用 “MON,WED,FRI” 將表示星期一、星期三和星期五。
Hyphen (-)
- 用於定義範圍。例如,9-17 表示上午 9 點到下午 5 點之間的每小時(含)。
Question mark (?)
? 可以使用問號代替 “*” 來將月份中的某一天或星期幾留空。
三、特性
預定義計劃
它是 cron 定義的一些預定義計劃,用來替代 cron 表達式。
Entry | Description | Equivalent To
----- | ----------- | -------------
@yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 1 1 *
@monthly | Run once a month, midnight, first of month | 0 0 1 * *
@weekly | Run once a week, midnight between Sat/Sun | 0 0 * * 0
@daily (or @midnight) | Run once a day, midnight | 0 0 * * *
@hourly | Run once an hour, beginning of hour | 0 * * * *
指定間隔
@every <duration>
以固定時間間隔運行,“@every 1h30m10s”表示在 1小時、30分鐘、10秒後激活的計劃,然後是此後的每個間隔。
時區
默認情況下,所有解釋和調度都在計算機的本地時區 (time.Local) 中完成。您可以在構建時指定不同的時區。
cron.New(
cron.WithLocation(time.UTC))
各個 cron 計劃還可以通過在 cron 規範的開頭提供額外的空格分隔字段(格式爲 “CRON_TZ=Asia/Tokyo”)來覆蓋它們要解釋的時區。
# Runs at 6am in time.Local
cron.New().AddFunc("0 6 * * ?", ...)
# Runs at 6am in America/New_York
nyc, _ := time.LoadLocation("America/New_York")
c := cron.New(cron.WithLocation(nyc))
c.AddFunc("0 6 * * ?", ...)
# Runs at 6am in Asia/Tokyo
cron.New().AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...)
# Runs at 6am in Asia/Tokyo
c := cron.New(cron.WithLocation(nyc))
c.SetLocation("America/New_York")
c.AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", ...)
作業包裝器
Cron 運行程序可以配置一系列作業包裝器,以向所有提交的作業添加橫切功能。
-
從作業中恢復所有異常(默認);
-
如果上一次運行尚未完成,則延遲作業的執行;
-
如果上一個運行尚未完成,則跳過作業的執行;
-
記錄每個作業的調用。
使用 cron.WithChain 選項爲添加到 cron 的所有作業安裝包裝器:
cron.New(cron.WithChain(
cron.SkipIfStillRunning(logger),
))
線程安全
由於 cron 服務與調用代碼同時運行,因此必須小心確保正確的同步。官方爲 cron 設計成了線程安全的,因此只要調用者確保調用之間有明確的發生之前順序。
日誌
cron 爲我們定義了一個 Logger 接口,它是 github.com/go-logr/logr 中定義的接口的子集。它有兩個日誌記錄級別(信息和錯誤),參數是鍵 / 值對。這使得 cron 日誌記錄能夠插入結構化日誌記錄系統。提供了一個適配器 [Verbose]PrintfLogger 來包裝標準庫 *log.Logger。爲了進一步瞭解 Cron 操作,可以激活詳細日誌記錄,這將記錄作業運行、調度決策以及添加或刪除的作業。
cron.New(
cron.WithLogger(
cron.VerbosePrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags))))
實施
Cron 條目存儲在一個數組中,按下次激活時間排序。Cron 會休眠,直到下一個作業運行爲止。
至此,cron 的介紹就到此爲止了,看了這些,基本上你也可以應對普通的定時任務了,當然,有不懂的地方你也可以自己翻閱一下官方文檔,描述還是很清晰的呢。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/HqDFNcsrHKptitiOSB4QyA