crontabex/crontab.go

352 lines
7.8 KiB
Go
Raw Normal View History

2018-12-06 03:07:16 +00:00
package crontab
import (
"errors"
"fmt"
"log"
"reflect"
2018-12-06 03:07:16 +00:00
"regexp"
"strings"
"time"
2019-01-01 17:59:34 +00:00
"github.com/satori/go.uuid"
2018-12-20 06:37:37 +00:00
"474420502.top/eson/structure/circular_linked"
2018-12-06 03:07:16 +00:00
"github.com/Pallinder/go-randomdata"
"github.com/davecgh/go-spew/spew"
)
// StatusType 设置状态的类型
type StatusType int
const (
_ StatusType = iota
// SExecuteOK 设置这个次成功或者失败的状态
SExecuteOK
// SExecuteSleep 设置下次Sleep时间, 只影响一次
SExecuteSleep
// SExecuteCrontab 设置改写Crontab, 覆盖以前的Crontab
SExecuteCrontab
)
2018-12-06 03:07:16 +00:00
// Force 强制下次执行
type Force struct {
sleep time.Duration
2018-12-06 03:07:16 +00:00
}
// NextTime 下次执行时间
func (force *Force) NextTime() time.Time {
return time.Now().Add(force.sleep)
2018-12-06 03:07:16 +00:00
}
// Crontab 的string解析
type Crontab struct {
crontab string
2019-01-01 17:59:34 +00:00
uid uuid.UUID
force *Force
2018-12-06 03:07:16 +00:00
min []timePointer
hour []timePointer
day []timePointer
month []timePointer
week []timePointer
WillPlans []time.Time
SkipPlans []time.Time
YearPlan *trieYear
2018-12-20 06:37:37 +00:00
interval *clinked.CircularLinked
lastStatus bool
isCalculated bool
2018-12-21 14:51:52 +00:00
trueCount int
failCount int
nextTime time.Time
2018-12-06 03:07:16 +00:00
}
// NewCrontab create 一个crontab
func NewCrontab(crontab string) *Crontab {
cron := &Crontab{}
cron.crontab = strings.TrimSpace(crontab)
cron.FromString(cron.crontab)
2018-12-06 03:07:16 +00:00
return cron
}
2018-12-06 19:02:19 +00:00
// UnmarshalYAML 添加序列化接口 Marshal没实现, 需要的时候可以自己添加
func (cron *Crontab) UnmarshalYAML(unmarshal func(interface{}) error) error {
var buf string
err := unmarshal(&buf)
if err != nil {
return nil
}
if err := cron.FromString(buf); err != nil {
return err
}
return nil
}
2018-12-20 06:37:37 +00:00
// SetStatus 设置上次状态 true false
func (cron *Crontab) SetStatus(statusType StatusType, statusValue ...interface{}) {
2018-12-06 03:07:16 +00:00
switch statusType {
case SExecuteOK:
if cron.interval != nil {
cron.lastStatus = statusValue[0].(bool)
}
case SExecuteSleep:
force := new(Force)
ivalue := statusValue[0]
switch value := ivalue.(type) {
case int:
force.sleep = time.Duration(value) * time.Second
case int64:
force.sleep = time.Duration(value) * time.Second
case time.Duration:
force.sleep = value
default:
panic(errors.New("statusValue type is error, the type is" + reflect.TypeOf(ivalue).String()))
}
cron.force = force
case SExecuteCrontab:
crontab := statusValue[0].(string)
cron.crontab = strings.TrimSpace(crontab)
cron.FromString(cron.crontab)
default:
panic(errors.New("StatusType is unknown, the type " + reflect.TypeOf(statusType).String()))
2018-12-06 03:07:16 +00:00
}
}
// // GetStatus 获取上次状态 true false
// func (cron *Crontab) GetStatus() (status bool) {
// return cron.lastStatus
// }
2018-12-20 20:18:40 +00:00
// TimeUp 是否时间快到, 时间间隔调用完TimeUp后必须调用NextTime, 为了精准控制时间
2018-12-06 03:07:16 +00:00
func (cron *Crontab) TimeUp() bool {
if cron.interval != nil {
return cron.intervalTimeUp()
}
return cron.linuxTimeUp()
}
// NextTime 返回下次任务的时间
2018-12-06 20:42:53 +00:00
func (cron *Crontab) NextTime() time.Time {
if cron.force != nil {
nt := cron.force.NextTime()
cron.force = nil
return nt
}
2018-12-06 03:07:16 +00:00
if cron.interval != nil {
2018-12-20 06:37:37 +00:00
if !cron.isCalculated {
now := time.Now()
cron.intervalCalculateNextTime(now)
}
2018-12-06 20:42:53 +00:00
return cron.nextTime
2018-12-06 03:07:16 +00:00
}
if len(cron.WillPlans) > 0 {
2018-12-06 20:42:53 +00:00
return cron.WillPlans[0]
2018-12-06 03:07:16 +00:00
}
2018-12-06 20:42:53 +00:00
return time.Now().Add(time.Second * 2)
2018-12-06 03:07:16 +00:00
}
func (cron *Crontab) String() string {
return fmt.Sprintf("min:%s\nhour:%s\nday:%s\nmonth:%s\nweek:%s\n", spew.Sdump(cron.min), spew.Sdump(cron.hour), spew.Sdump(cron.day), spew.Sdump(cron.month), spew.Sdump(cron.week))
}
// FromString 解析crontab 的 表达式
func (cron *Crontab) FromString(crontab string) error {
crontab = cron.crontab
2019-01-01 17:59:34 +00:00
uid, err := uuid.NewV4()
if err != nil {
panic(err)
}
cron.uid = uid
cron.interval = nil
cron.min = nil
cron.hour = nil
cron.day = nil
cron.month = nil
cron.week = nil
cron.WillPlans = nil
cron.SkipPlans = nil
cron.YearPlan = nil
2018-12-06 03:07:16 +00:00
matches := regexp.MustCompile("[^ ]+").FindAllString(crontab, -1)
mlen := len(matches)
switch mlen {
case 1:
// "f1-2|5-10x5,f1|10m,10-15,f1"
2018-12-06 19:02:19 +00:00
2018-12-06 03:07:16 +00:00
cron.lastStatus = true
cron.isCalculated = true
cron.nextTime = time.Now()
2018-12-06 03:07:16 +00:00
cron.interval = clinked.NewCircularLinked()
var intervalList []interface{}
intervalList = parseIntervalString(matches[0])
cron.interval.Append(intervalList...)
2018-12-06 03:07:16 +00:00
case 5:
cron.min = createTimePointer(matches[0], 0, 59, true)
cron.hour = createTimePointer(matches[1], 0, 23, true)
cron.day = createTimePointer(matches[2], 1, 31, false)
cron.month = createTimePointer(matches[3], 1, 12, true)
cron.week = createTimePointer(matches[4], 0, 6, true)
cron.createYearPlan()
cron.TimeUp()
2018-12-06 03:07:16 +00:00
default:
return errors.New("mathches len != want, check crontab string")
}
return nil
}
// createYearPlan 创建年度计划
func (cron *Crontab) createYearPlan() {
cron.YearPlan = newTrieYear()
cron.YearPlan.FromCrontab(cron)
}
func (cron *Crontab) linuxTimeUp() bool {
now := time.Now()
maxlen := 1000
createlen := 500
plen := len(cron.WillPlans)
2018-12-25 02:17:15 +00:00
if plen <= createlen { // 如果当前生成的计划表少于 限制的500. 就生成新的计划表
2018-12-06 03:07:16 +00:00
var lastplan time.Time
if plen == 0 {
lastplan = now
} else {
lastplan = cron.WillPlans[plen-1].Add(time.Minute)
}
if !cron.YearPlan.CheckYear() {
cron.createYearPlan()
}
timeplans := cron.YearPlan.GetPlanTime(cron, lastplan, uint(maxlen-plen))
cron.WillPlans = append(cron.WillPlans, timeplans...)
}
if len(cron.WillPlans) > 0 {
istimeup := false
for i := 0; i < maxlen; i++ {
2018-12-25 02:17:15 +00:00
// 统计过了多少计划任务时间表 i - 1
2018-12-06 03:07:16 +00:00
if now.Unix() >= cron.WillPlans[i].Unix() {
istimeup = true
} else {
2018-12-25 02:17:15 +00:00
// 清除过时的计划任务时间表
2018-12-06 03:07:16 +00:00
if istimeup {
if i-1 > 0 {
cron.SkipPlans = append(cron.SkipPlans, cron.WillPlans[0:i-1]...)
if len(cron.SkipPlans) >= maxlen+200 {
cron.SkipPlans = cron.SkipPlans[200:]
}
}
cron.WillPlans = cron.WillPlans[i:]
return istimeup
}
return istimeup
}
}
2018-12-25 02:17:15 +00:00
// 如果所有计划表都不符合, 全部放到忽略的计划表上, 这个表方便打印查看, 因为程序执行超时过了多少计划任务
2018-12-06 03:07:16 +00:00
cron.SkipPlans = append(cron.SkipPlans, cron.WillPlans...)
cron.WillPlans = nil
return istimeup
}
log.Panicln("error willplans range")
return false
}
2018-12-20 06:37:37 +00:00
// IntervalCalculateNextTime 计算时间间隔的下次时间
func (cron *Crontab) intervalCalculateNextTime(now time.Time) {
2018-12-21 14:51:52 +00:00
iv := cron.interval.Cursor().GetValue().(*hInterval)
2018-12-20 06:37:37 +00:00
isecond := 0
2018-12-20 10:42:18 +00:00
2018-12-21 14:51:52 +00:00
if iv.PlanFailCount.Size() == 0 && len(iv.PlanFail) == 0 {
cron.lastStatus = true
2018-12-20 10:42:18 +00:00
}
2018-12-21 14:51:52 +00:00
if cron.lastStatus {
cron.trueCount++
cron.failCount = 0
2018-12-20 10:42:18 +00:00
2018-12-21 14:51:52 +00:00
isecond = intervalPriorityListISecond(&iv.PlanTrueCount, cron.trueCount)
2018-12-20 20:18:40 +00:00
if isecond == -1 {
2018-12-21 14:51:52 +00:00
if len(iv.PlanTrue) > 0 {
idx := randomdata.Number(len(iv.PlanTrue))
lr := iv.PlanTrue[idx]
2018-12-20 20:18:40 +00:00
isecond = randomdata.Number(lr.left, lr.right+1)
2018-12-21 14:51:52 +00:00
2018-12-20 20:18:40 +00:00
} else {
isecond = 0
}
2018-12-21 14:51:52 +00:00
2018-12-20 10:42:18 +00:00
}
2019-01-01 19:03:28 +00:00
fmt.Println(time.Now().Format("01-02 15:04:05"), cron.uid.String(), "success:", cron.trueCount, " wait:", isecond)
2018-12-25 02:17:15 +00:00
2018-12-20 10:42:18 +00:00
} else {
2018-12-20 20:18:40 +00:00
2018-12-21 14:51:52 +00:00
cron.failCount++
cron.trueCount = 0
isecond = intervalPriorityListISecond(&iv.PlanFailCount, cron.failCount)
2018-12-20 20:18:40 +00:00
if isecond == -1 {
2018-12-21 14:51:52 +00:00
if len(iv.PlanFail) > 0 {
idx := randomdata.Number(len(iv.PlanFail))
lr := iv.PlanFail[idx]
2018-12-20 20:18:40 +00:00
isecond = randomdata.Number(lr.left, lr.right+1)
} else {
isecond = 0
}
}
2018-12-21 14:51:52 +00:00
2019-01-01 19:03:28 +00:00
fmt.Println(time.Now().Format("01-02 15:04:05"), cron.uid.String(), "fail:", cron.failCount, " wait:", isecond)
2018-12-20 06:37:37 +00:00
}
2018-12-06 03:07:16 +00:00
2018-12-20 06:37:37 +00:00
iv.Count--
if iv.Count <= 0 {
iv.reset()
cron.interval.MoveNext()
}
2018-12-20 20:18:40 +00:00
cron.isCalculated = true
2018-12-20 06:37:37 +00:00
cron.nextTime = now.Add(time.Duration(isecond) * time.Second)
}
2018-12-06 03:07:16 +00:00
2018-12-20 06:37:37 +00:00
func (cron *Crontab) intervalTimeUp() bool {
2018-12-06 03:07:16 +00:00
if cron.isCalculated { // 需要调用nexttime()才能正常正常计算下次的时间
2018-12-20 20:18:40 +00:00
now := time.Now()
if now.Unix() >= cron.nextTime.Unix() {
cron.isCalculated = false
return true
}
2018-12-06 03:07:16 +00:00
}
return false
}