Golang 實現簡單的定時器
阿新 • • 發佈:2019-01-02
問題
做專案的時候經常會有這樣的需求,在某個時刻開始執行某個任務,然後每隔一段時間都會執行該任務。
windows下有計劃任務,linux下有cron。如果用python可以使用apscheduler庫。那麼在Go中應該怎麼實現呢?
間隔執行
time包中有個Ticker可以用來實現簡單的定時任務。
ticker := time.NewTicker(5 * time.Second)
for _ = range ticker.C {
fmt.Println(time.Now())
}
Ticker會在每隔一段時間執行,比如上面的例子中,每隔5秒列印一下當前時間。
但是,這顯然滿足不了我們的需求,我們還需要在某個固定時刻才開始。
最終思路
這裡提供一種比較簡單的思路。對於固定時刻T,計算T和當前時間的時間差,然後sleep到T,然後用Tikcer開始定時任務,每隔時間間隔D執行一次任務。
以什麼樣的形式提供固定時刻T以及時間間隔D
一個首要原則就是越簡單越好,最好提供一個原生的包就能解析的。
在time包中,解析時間有ParseInLocation,解析時間間隔有ParseDuration。那麼我們就可以用這兩個方法能解析的形式來表達T和D。
T的格式
time包預定義了一些格式。
const (
ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
RFC822 = "02 Jan 06 15:04 MST"
RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
// Handy time stamps.
Stamp = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
StampMicro = "Jan _2 15:04:05.000000"
StampNano = "Jan _2 15:04:05.000000000"
)
這裡吐槽一下,一開始不明白為什麼是2006-01-02 15:04:05這個點,一直在想那個點到底發生了什麼重大的事。後來才知道,按照美式的時間格式,也就是上面的ANSIC,月,日,時,分,秒,年,排列起來正好是123456。這麼設計是為了方便記憶。。。
當然也可以自定義時間格式,比如2006-01-02 15:04:05
。一般在我們的定時任務中,我們常用的是時分秒這樣的時刻,所以T得表達方式就定義為:
15:04:05
D的格式
D的格式比較簡單,可以使用300ms, -1.5h, 2h45m
這種格式。詳情見ParseDuration的函式說明。
程式碼
// sched to start scheduler job at start time by interval duration.
func sched(jobFunc interface{}, start, interval string, jobArgs ...interface{}) {
jobValue := reflect.ValueOf(jobFunc)
if jobValue.Kind() != reflect.Func {
log.Panic("only function can be schedule.")
}
if len(jobArgs) != jobValue.Type().NumIn() {
log.Panic("The number of args valid.")
}
// Get job function args.
in := make([]reflect.Value, len(jobArgs))
for i, arg := range jobArgs {
in[i] = reflect.ValueOf(arg)
}
// Get interval d.
d, err := time.ParseDuration(interval)
if err != nil {
log.Panic(err)
}
location, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
log.Panic(err)
}
t, err := time.ParseInLocation("15:04:05", start, location)
if err != nil {
log.Panic(err)
}
now := time.Now()
// Start time.
t = time.Date(now.Year(), now.Month(), now.Day(), t.Hour(), t.Minute(), t.Second(), 0, location)
if now.After(t) {
t = t.Add((now.Sub(t)/d + 1) * d)
}
time.Sleep(t.Sub(now))
go jobValue.Call(in)
ticker := time.NewTicker(d)
go func() {
for _ = range ticker.C {
go jobValue.Call(in)
}
}()
}
結語
我只想說,感謝goroutine。