問題
做項目的時候經(jīng)常會有這樣的需求瞬浓,在某個時刻開始執(zhí)行某個任務(wù)初婆,然后每隔一段時間都會執(zhí)行該任務(wù)。
windows下有計劃任務(wù)猿棉,linux下有cron磅叛。如果用python可以使用apscheduler庫。那么在Go中應(yīng)該怎么實現(xiàn)呢萨赁?
間隔執(zhí)行
time包中有個Ticker可以用來實現(xiàn)簡單的定時任務(wù)弊琴。
ticker := time.NewTicker(5 * time.Second)
for _ = range ticker.C {
fmt.Println(time.Now())
}
Ticker會在每隔一段時間執(zhí)行,比如上面的例子中杖爽,每隔5秒打印一下當(dāng)前時間敲董。
但是,這顯然滿足不了我們的需求慰安,我們還需要在某個固定時刻才開始腋寨。
最終思路
這里提供一種比較簡單的思路。對于固定時刻T化焕,計算T和當(dāng)前時間的時間差萄窜,然后sleep到T,然后用Tikcer開始定時任務(wù)锣杂,每隔時間間隔D執(zhí)行一次任務(wù)脂倦。
以什么樣的形式提供固定時刻T以及時間間隔D
一個首要原則就是越簡單越好番宁,最好提供一個原生的包就能解析的元莫。
在time包中,解析時間有ParseInLocation蝶押,解析時間間隔有ParseDuration踱蠢。那么我們就可以用這兩個方法能解析的形式來表達T和D。
T的格式
time包預(yù)定義了一些格式棋电。
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這個點,一直在想那個點到底發(fā)生了什么重大的事赶盔。后來才知道企锌,按照美式的時間格式,也就是上面的ANSIC于未,月撕攒,日陡鹃,時,分抖坪,秒萍鲸,年,排列起來正好是123456擦俐。這么設(shè)計是為了方便記憶脊阴。。蚯瞧。
當(dāng)然也可以自定義時間格式嘿期,比如2006-01-02 15:04:05
。一般在我們的定時任務(wù)中埋合,我們常用的是時分秒這樣的時刻秽五,所以T得表達方式就定義為:
15:04:05
D的格式
D的格式比較簡單,可以使用300ms, -1.5h, 2h45m
這種格式饥悴。詳情見ParseDuration的函數(shù)說明坦喘。
代碼
// 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)
}
}()
}
結(jié)語
我只想說,感謝goroutine西设。