go 定時任務庫 cron


簡介

在Linux中,Cron是計划任務管理系統,通過crontab命令使任務在約定的時間執行已經計划好的工作,例如定時備份系統數據、周期性清理緩存、定時重啟服務等。

本文介紹的cron庫是一個用於管理定時任務的庫,就是用Go實現Linux中crontab命令的相似效果。

快速使用

文本代碼使用 Go Modules

創建目錄並初始化:

$ mkdir cron && cd cron
$ go mod init cron

安裝cron,目前最新穩定版本為 v3:

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

在項目中導入包使用:

package main

import (
  "fmt"
  "time"

  "github.com/robfig/cron/v3"
)

func main() {
  c := cron.New()

  c.AddFunc("@every 1s", func() {
    fmt.Println("tick every 1 second")
  })

  c.Start()
  
  // 阻塞,或者使用其他延遲時間函數、
  select{}
}

使用非常簡單,創建cron對象,這個對象用於管理定時任務。

調用cron對象的AddFunc()方法向管理器中添加定時任務。AddFunc()接受兩個參數,參數 1 以字符串形式指定觸發時間規則,參數 2 是一個無參的函數,每次觸發時調用。@every 1s表示每秒觸發一次,@every后加一個時間間隔,表示每隔多長時間觸發一次。例如@every 1h表示每小時觸發一次,@every 1m2s表示每隔 1 分 2 秒觸發一次。time.ParseDuration()支持的格式都可以用在這里。

調用c.Start()啟動定時循環。

注意一點,因為c.Start()啟動一個新的 goroutine 做循環檢測,我們在代碼最后加了一行time.Sleep(time.Second * 5)防止主 goroutine 退出。

運行效果,每隔 1s 輸出一行字符串:

$ go run main.go
tick every 1 second
tick every 1 second
tick every 1 second
tick every 1 second
tick every 1 second

時間格式

與Linux 中crontab命令相似,cron庫支持用 5 個空格分隔的域來表示時間。

字段名 是否必須 允許的值 允許的特定字符
秒(Seconds) 0-59 * / , -
分(Minute) 0-59 * / , -
時(Hours) 0-23 * / , -
日(Day of month) 1-31 * / , - ?
月(Month) 1-12 或 JAN-DEC * / , -
星期(Day of week) 0-6 或 SUM-SAT * / , - ?

注意,月份和周歷名稱都是不區分大小寫的,也就是說SUN/Sun/sun表示同樣的含義(都是周日)。

特殊字符含義如下:

  • 星號():使用的域可以匹配任何值,例如將月份域(第 5 個)設置為*,表示每個月;
  • 斜線(/):用來指定范圍的步長,例如如第1個字段(minutes) 值是 3-59/15,表示每小時的第3分鍾開始執行一次,之后每隔 15 分鍾執行一次(即 3、18、33、48 這些時間點執行),這里也可以表示為:3/15;
  • 逗號(,):用來列舉一些離散的值和多個范圍,例如將周歷的域(第 6 個)設置為MON,WED,FRI表示周一、三和五;
  • 連字號(-):用來表示范圍,例如將小時的域(第 3 個)設置為9-17表示上午 9 點到下午 17 點(包括 9 和 17);
  • 問號(?):只能用在月歷和周歷的域中,用來代替*,表示每月/周的任意一天。
  • (L,W,#): Go中沒有L,W,#的用法

了解規則之后,我們可以定義任意時間:

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

記熟了這幾個域的順序,再多練習幾次很容易就能掌握格式。熟悉規則了之后,就能熟練使用crontab命令了。

func main() {
  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("On the half hour of 3-6am, 8-11pm")
  })

  c.AddFunc("0 0 1 1 *", func() {
    fmt.Println("Jun 1 every year")
  })

  c.Start()

  for {
    time.Sleep(time.Second)
  }
}

預定義時間規則

為了方便使用,cron預定義了一些時間規則:

  • @yearly:也可以寫作@annually,表示每年第一天的 0 點。等價於0 0 1 1 *;
  • @monthly:表示每月第一天的 0 點。等價於0 0 1 * *;
  • @weekly:表示每周第一天的 0 點,注意第一天為周日,即周六結束,周日開始的那個 0 點。等價於0 0 * * 0;
  • @daily:也可以寫作@midnight,表示每天 0 點。等價於0 0 * * *;
  • @hourly:表示每小時的開始。等價於0 * * * *。

例如:

func main() {
  c := cron.New()

  c.AddFunc("@hourly", func() {
    fmt.Println("Every hour")
  })

  c.AddFunc("@daily", func() {
    fmt.Println("Every day on midnight")
  })

  c.AddFunc("@weekly", func() {
    fmt.Println("Every week")
  })

  c.Start()

  for {
    time.Sleep(time.Second)
  }
}

上面代碼只是演示用法,實際運行可能要等待非常長的時間才能有輸出。

注意:這樣使用一個 cron.New() 的定時任務,執行的每個方法是順序執行的,也就是說並不是同一時刻開始執行

固定時間間隔

cron支持固定時間間隔,格式為:

@every <duration>

含義為每隔duration觸發一次。<duration>會調用time.ParseDuration()函數解析,所以ParseDuration支持的格式都可以。例如1h30m10s

時區

默認情況下,所有時間都是基於當前時區的。當然我們也可以指定時區,有 2 兩種方式:

  • 在時間字符串前面添加一個CRON_TZ= + 具體時區,具體時區的格式在之前carbon的文章中有詳細介紹。東京時區為Asia/Tokyo,紐約時區為America/New_York
  • 創建cron對象時增加一個時區選項cron.WithLocation(location)locationtime.LoadLocation(zone)加載的時區對象,zone為具體的時區格式。或者調用已創建好的cron對象的SetLocation()方法設置時區。

示例:

func main() {
  nyc, _ := time.LoadLocation("America/New_York")
  c := cron.New(cron.WithLocation(nyc))
  c.AddFunc("0 6 * * ?", func() {
    fmt.Println("Every 6 o'clock at New York")
  })

  c.AddFunc("CRON_TZ=Asia/Tokyo 0 6 * * ?", func() {
    fmt.Println("Every 6 o'clock at Tokyo")
  })

  c.Start()

  for {
    time.Sleep(time.Second)
  }
}

巨人的肩膀

Go 每日一庫之 cron
Go 每日一庫之定時任務庫:cron
Go -- cron定時任務的用法


免責聲明!

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



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