一、cron表达式
cron表达式,主要用于定时作业(定时任务)系统定义执行时间或执行频率的表达式。Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:
Seconds Minutes Hours DayofMonth Month DayofWeek Year或
Seconds Minutes Hours DayofMonth Month DayofWeek
从右边看,就是年+周几+月日时分秒
1.例子
0 0 3 * * ? 每天3点执行0 5/10 3 * * ?0 10 3 ? * 1*/5 * * * * ?0 */1 * * * ?0 0/3 * * * ?0 0/5 14 * * ?0 0 5-15 * * ?0 0 10,14,16 * * ?0 10 3 ? * 1#3
问号只能出现在日期和星期这两个位置,表示这个位置的值不确定。这里简单写一下自己对*和?的理解,举个简单例子,如果我们指定每月2号执行操作,那么每月2号是星期几是不确定的,因此星期字段不能为* ,*表示所有的值,所以要用?,即我不关心2号是周几,它爱是周几是周几。
0 15 10 ? * 6L 2002-20050 0 23 L * ?
package main
//blog: xiaorui.cc
import (
"github.com/robfig/cron"
"log"
)
func main() {
i := 0
c := cron.New()
spec := "0 */1 * * * *"
c.AddFunc(spec, func() {
i++
log.Println("start", i)
})
c.Start()
select{} //阻塞主线程不退出
}
AddFunc注册任务到调度器里,当任务要执行的时候会使用goroutines调用,这样每个任务都不会发生阻塞。其中注意select的用法: golang 的 select 的功能和 select, poll, epoll 相似, 就是监听 IO 操作,当 IO 操作发生时,触发相应的动作。
三、leaf的AfterFunc
Timer主要是提供一个Cron功能的定时器服务,其中Timer是time.AfterFunc的封装,是为了方便聚合到Skeleton中。
Go 语言标准库提供了定时器的支持:
func AfterFunc(d Duration, f func()) *Timer
AfterFunc 会等待 d 时长后调用 f 函数,这里的 f 函数将在另外一个 goroutine 中执行。Leaf 提供了一个相同的 AfterFunc 函数,相比之下,f 函数在 AfterFunc 的调用 goroutine 中执行,这样就避免了同步机制的使用:
skeleton.AfterFunc(5 * time.Second, func() {
// ...
})
这个功能也是通过一个通道来做的。
type Dispatcher struct {
ChanTimer chan *Timer
}
func NewDispatcher(l int) *Dispatcher {
disp := new(Dispatcher)
disp.ChanTimer = make(chan *Timer, l)
return disp
}
// Timer
type Timer struct {
t *time.Timer
cb func()
}
func (disp *Dispatcher) AfterFunc(d time.Duration, cb func()) *Timer {
t := new(Timer)
t.cb = cb
t.t = time.AfterFunc(d, func() {
disp.ChanTimer <- t
})
return t
}
实质上构造了一个Timer类型,包含原生的time.Timer和到期执行的回调cb。然后原生Timer时间到了之后,把这个构造数据塞入通道disp.ChanTimer。在skeleton.Run里,会自动执行这些Cb
// leaf\module\skeleton.go
func (s *Skeleton) Run(closeSig chan bool) {
for {
select {
case <-closeSig:
s.commandServer.Close()
s.server.Close()
for !s.g.Idle() || !s.client.Idle() {
s.g.Close()
s.client.Close()
}
return
case ri := <-s.client.ChanAsynRet:
s.client.Cb(ri)
// 等待来自通道的数据
case ci := <-s.server.ChanCall:
s.server.Exec(ci)
case ci := <-s.commandServer.ChanCall:
s.commandServer.Exec(ci)
case cb := <-s.g.ChanCb:
s.g.Cb(cb)
case t := <-s.dispatcher.ChanTimer:
t.Cb()
}
}
}
(<-d.ChanTimer).Cb()
func ExampleTimer() {
d := timer.NewDispatcher(10)
// timer 1
d.AfterFunc(1, func() {
fmt.Println("My name is Leaf")
})
// timer 2
t := d.AfterFunc(1, func() {
fmt.Println("will not print")
})
t.Stop()
// dispatch
(<-d.ChanTimer).Cb()
// Output:
// My name is Leaf
}
四、leaf的cron功能
看起来,是在github.com/robfig/cron库的基础上来做的。源码如下
// Cron
type Cron struct {
t *Timer
}
func (c *Cron) Stop() {
if c.t != nil {
c.t.Stop()
}
}
func (disp *Dispatcher) CronFunc(cronExpr *CronExpr, _cb func()) *Cron {
c := new(Cron)
now := time.Now()
nextTime := cronExpr.Next(now)
if nextTime.IsZero() {
return c
}
// callback
var cb func()
cb = func() {
defer _cb()
now := time.Now()
nextTime := cronExpr.Next(now)
if nextTime.IsZero() {
return
}
c.t = disp.AfterFunc(nextTime.Sub(now), cb)
}
c.t = disp.AfterFunc(nextTime.Sub(now), cb)
return c
}
源码中使用了嵌套的倒计时调用cb。
func ExampleCron() {
d := timer.NewDispatcher(10)
// cron expr
cronExpr, err := timer.NewCronExpr("0/3 * * * * *")
if err != nil {
fmt.Println("cron expr error:",err)
return
}
// cron
var c *timer.Cron
c = d.CronFunc(cronExpr, func() {
fmt.Println("My name is Leaf")
//c.Stop()
})
fmt.Println("begin cron",c)
// dispatch
(<-d.ChanTimer).Cb()
select{
case t := <-d.ChanTimer:
t.Cb()
}
// Output:
// My name is Leaf
}
My name is Leaf
cronExpr, err := timer.NewCronExpr("0/3 * * * * *")
now := time.Now()
nextTime := cronExpr.Next(now)
if nextTime.IsZero() {
fmt.Println("nextTime is Zero")
}
fmt.Println("sub time",nextTime.Sub(now))
这个时间也不准呀:
sub time 1.8513635s