1. linux基本格式:
* * * * *
分(0-59) 时(0-23) 日(1-31) 月(1-12) 星期(0-7)
①. *表示任意分、时、日、月、星期
②. ,表示并行
③. -表示连续,[m, n]闭区间,执行了(m - n + 1)次
④. /表示整除
(1). 举例:
20 * * * * shell 每个小时的第20分钟执行
20,40 * * * * shell 每个小时的第20分钟、第40分钟执行
20-40 * * * * shell 每个小时的第20分钟到第40分钟(共21次)执行
*/2 * * * * shell 每个小时的分钟可以被2整除的都会(0, 2, 4, ..., 58)执行
*/5 * * * * echo hello > /tmp/x.log 每隔5分钟执行1次
1-5 * * * * /usr/bin/python /data/x.py 第1-5分钟执行5次
0 10,22 * * * echo bye | tail -1 每天10点22点整执行1次
(2). 整除:
*/2 0, 2, 4, ..., 58 被2整除的分钟
0/2 0, 2, 4, ..., 58 等价上面
1/2 1, 3, 5, ..., 59 前面指定了特定的数字,表示时间点减去这个数字,再看能不能被2整除
7/2 7, 9, 11, ..., 59 时间点减去这个数字,如果是负数,就不考虑了.
(3). 坑:
①. 星期:
a. 由于linux版本的问题,有的版本星期有些可能是0-6,有些可能是1-7,有些1代表星期天
b. 可以用简写来代替,Cron支持配置星期缩写:MON TUE WED THU FRI SAT SUN
* * * * * MON-FRI
c. 还可以用W指定工作日(周一到周五)
②. 月份:
a. 简写:
一月Jan、二月Feb、三月Mar、四月Apr、五月May、六月Jun、七月Jul、八月Aug、九月Sep、十月Oct、十一月Nov、十二月Dec
③. 日:
a. 指定L字段表示每个月最后一天
(4). 怎么每秒执行?
* * * * * shell
* * * * * sleep 1;shell
...
* * * * * sleep 59;shell
注:
两个59个脚本都是在同一分钟执行,由于sleep了.就会从0秒到59秒都有脚本执行.
(5). cron执行原理:
已知当前UNIX时间为1532659200,即2018/07/27 10:40:00
已知道Cron表达式是30 * * * *
如何计算命令的下次调度时间?
当前时间: 2018/07/2710:40:00
40分 10时 27日
↓ +20
00分 11时 27日
↓ +30
30分 11时 27日
下次调度时间:2018/07/27 11:30:00
Cron: 30 * * * *
枚举:
分钟: [З0]
小时: [0,1,2..23]
日期: [1,2,.31]
月份: [1,2...12]
4. go的cron解析库(Cronexpr库):
(1). 安装:
go get -u "github.com/gorhill/cronexpr"
(2). 示例1:
import "github.com/gorhill/cronexpr"
func main() {
var (
expr *cronexpr.Expression
err error
now time.Time
nextTime time.Time
)
// 7位表达式:增加了秒、和年(2018 - 2099)
if expr, err = cronexpr.Parse("*/5 * * * * * *"); err != nil { // Parse():解析与校验Cron表达式
fmt.Print(err)
return
}
now = time.Now() // 当前时间
nextTime = expr.Next(now) // 下次调度时间,Next()方法是根据当前时间,计算下次调度时间.
fmt.Print(now, nextTime)
// */5(每5分钟执行一次):
// 2020-07-05 21:24:00 2020-07-05 21:25:00
// */6(每6分钟执行一次):
// 2020-07-05 21:25:25 2020-07-05 21:30:00
// 0, 6, 12, 18, ..., 48
// 等待定时器超时
time.AfterFunc(nextTime.Sub(now), func () { // 计算差值
fmt.Print("被调度了")
})
// 需要阻塞主进程,否则上面没执行完就主进程退出了
time.Sleep(5* time.Second)
}
(3). 示例:
type CronJob struct {
expr *cronexpr.Expression
nextTime time.Time
}
func main() {
// 把所有的Cron任务存放到一个地方
// 需要1个调度协程,它定时检查/扫描所有的Cron任务,谁过期就执行谁
var (
cronJob *CronJob
expr *cronexpr.Expression
now time.Time
scheduleTable map[string]*CronJob // key是任务名字
)
scheduleTable = make(map[string]*CronJob)
now = time.Now()
// MustParse进行断言,觉对不会出错
expr = cronexpr.MustParse("*/5 * * * * * *")
cronJob = &CronJob{
expr: expr,
nextTime: expr.Next(now),
}
// 任务注册到调度表中
scheduleTable["job1"] = cronJob
// 启动一个调度协程
go func() {
var (
jobName string
cronJob *CronJob
now time.Time
)
// 定时检查一下任务调度表
for {
now = time.Now()
for jobName, cronJob = range scheduleTable {
// 判断是否过期
if cronJob.nextTime.Before(now) || cronJob.nextTime.Equal(now) {
// 启动一个协程,执行任务
go func(jobName string) {
fmt.Print(jobName)
}(jobName)
// 计算下一次调度时间
cronJob.nextTime = cronJob.expr.Next(now)
fmt.Println(jobName, cronJob.nextTime)
}
}
// 睡眠100毫秒
select {
case <- time.NewTimer(100 * time.Millisecond).C: // 将在100毫秒可读,返回
}
}
}()
time.Sleep(100 * time.Second)
}