1. 完成crontab 基本结构, 并添加间隔的定义方法(含随机状态)

2. TODO: 完善主框架之间的连用
This commit is contained in:
huangsimin 2018-12-05 18:40:06 +08:00
parent 60f5aaad3f
commit 46e451cc42
3 changed files with 443 additions and 9 deletions

View File

@ -9,9 +9,27 @@ import (
"strings"
"time"
"github.com/Pallinder/go-randomdata"
"github.com/davecgh/go-spew/spew"
)
type randLR struct {
left, right int
}
type hInterval struct {
PlanFail []randLR
PlanNormal []randLR
Count int
ConstCount int
}
func (interval *hInterval) reset() {
interval.Count = interval.ConstCount
}
type timePointer struct {
left, right int
leftlimit, rightlimit int
@ -35,6 +53,10 @@ type Crontab struct {
SkipPlans []time.Time
YearPlan *trieYear
interval *CircularLinked
lastStatus bool
nextTime time.Time
}
// NewCrontab create 一个crontab
@ -44,6 +66,31 @@ func NewCrontab(crontab string) *Crontab {
return cron
}
// SetStatus 设置状态 接口定义
func (cron *Crontab) SetStatus(status interface{}) {
if cron.interval != nil {
cron.lastStatus = status.(bool)
}
}
// GetStatus 设置状态 接口定义
func (cron *Crontab) GetStatus() (status interface{}) {
if cron.interval != nil {
return cron.lastStatus
}
return nil
}
// TimeUp 是否时间快到
func (cron *Crontab) TimeUp() bool {
if cron.interval != nil {
return cron.intervalTimeUp()
}
return cron.linuxTimeUp()
}
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))
}
@ -56,6 +103,13 @@ func (cron *Crontab) FromString(crontab string) error {
mlen := len(matches)
switch mlen {
case 1:
// "f1-2|5-10x5,f1|10m,10-15,f1"
cron.nextTime = time.Now()
cron.lastStatus = true
cron.interval = NewCircularLinked()
var intervalList []interface{}
intervalList = parseIntervalString(matches[0])
cron.interval.Append(intervalList...)
case 5:
cron.min = createTimePointer(matches[0], 0, 59, true)
@ -64,7 +118,7 @@ func (cron *Crontab) FromString(crontab string) error {
cron.month = createTimePointer(matches[3], 1, 12, true)
cron.week = createTimePointer(matches[4], 0, 6, true)
cron.CreateYearPlan()
cron.createYearPlan()
default:
return errors.New("mathches len != want, check crontab string")
}
@ -72,15 +126,13 @@ func (cron *Crontab) FromString(crontab string) error {
return nil
}
// CreateYearPlan 创建年度计划
func (cron *Crontab) CreateYearPlan() {
// createYearPlan 创建年度计划
func (cron *Crontab) createYearPlan() {
cron.YearPlan = newTrieYear()
cron.YearPlan.FromCrontab(cron)
}
// TimeUp 是否时间快到
func (cron *Crontab) TimeUp() bool {
func (cron *Crontab) linuxTimeUp() bool {
now := time.Now()
maxlen := 1000
createlen := 500
@ -94,7 +146,7 @@ func (cron *Crontab) TimeUp() bool {
lastplan = cron.WillPlans[plen-1].Add(time.Minute)
}
if !cron.YearPlan.CheckYear() {
cron.CreateYearPlan()
cron.createYearPlan()
}
timeplans := cron.YearPlan.GetPlanTime(cron, lastplan, uint(maxlen-plen))
@ -130,6 +182,36 @@ func (cron *Crontab) TimeUp() bool {
return false
}
func (cron *Crontab) intervalTimeUp() bool {
now := time.Now()
if now.Unix() >= cron.nextTime.Unix() {
iv := cron.interval.Cursor().GetValue().(hInterval)
isecond := 0
if cron.lastStatus == false && len(iv.PlanFail) > 0 {
idx := randomdata.Number(len(iv.PlanFail))
lr := iv.PlanFail[idx]
isecond = randomdata.Number(lr.left, lr.right+1)
} else {
idx := randomdata.Number(len(iv.PlanNormal))
lr := iv.PlanNormal[idx]
isecond = randomdata.Number(lr.left, lr.right+1)
}
iv.Count--
if iv.Count <= 0 {
iv.reset()
cron.interval.MoveNext()
}
cron.nextTime = now.Add(time.Duration(isecond) * time.Second)
return true
}
return false
}
func createTimePointer(min string, llimit, rlimit int, fixedLeftRight bool) []timePointer {
var result []timePointer
@ -217,3 +299,98 @@ func createTimePointer(min string, llimit, rlimit int, fixedLeftRight bool) []ti
return result
}
func parseIntervalString(crontab string) []interface{} {
var result []interface{}
values := strings.Split(crontab, ",")
for _, value := range values {
interval := hInterval{}
// 次数
valuesCounts := strings.Split(value, "x")
switch len(valuesCounts) {
case 1:
interval.ConstCount = 1
case 2:
count, err := strconv.Atoi(valuesCounts[1])
if err != nil {
panic(err)
}
interval.ConstCount = count
default:
panic("valuesCounts error, the len is not in range")
}
// 统计失败与普通间隔值的数组
failAndNormal := valuesCounts[0]
valuesFN := strings.Split(failAndNormal, "|")
for _, FN := range valuesFN {
if FN == "" {
continue
}
switch FN[0] {
case 'f', 'F':
fvalue := FN[1:]
interval.PlanFail = append(interval.PlanFail, parseRandLR(fvalue))
case 'n':
value := FN[1:]
interval.PlanNormal = append(interval.PlanNormal, parseRandLR(value))
default:
interval.PlanNormal = append(interval.PlanNormal, parseRandLR(FN))
}
}
interval.reset()
result = append(result, interval)
}
return result
}
func parseRandLR(value string) randLR {
vlen := len(value)
lastchar := value[vlen-1]
lr := strings.Split(value, "-")
switch len(lr) {
case 1:
lr := randLR{parseTimeValue(lr[0], lastchar), parseTimeValue(lr[0], lastchar)}
return lr
case 2:
lr := randLR{parseTimeValue(lr[0], lastchar), parseTimeValue(lr[1], lastchar)}
return lr
default:
panic("lr is error")
}
}
func getInt(v string) int {
vint, err := strconv.Atoi(v)
if err != nil {
panic(err)
}
return vint
}
func parseTimeValue(v string, lastchar byte) int {
vlen := len(v)
switch lastchar {
case 's':
return getInt(v[:vlen-1])
case 'm':
return getInt(v[:vlen-1]) * 60
case 'h':
return getInt(v[:vlen-1]) * 3600
case 'd':
return getInt(v[:vlen-1]) * 3600 * 24
default:
return getInt(v)
}
}

View File

@ -25,7 +25,7 @@ func TestParseCrontab(t *testing.T) {
t.Error("GetPlanTime error len != 10")
}
cron.CreateYearPlan()
cron.createYearPlan()
if !cron.TimeUp() {
t.Error("timeup error")
}
@ -34,6 +34,26 @@ func TestParseCrontab(t *testing.T) {
}
func TestParseInterval(t *testing.T) {
// crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围
// t.Error("")
// crontab := "f1-2|1-3|5-8x5,f1|10m,10-15,f1"
// PrintMemUsage()
// cron := NewCrontab(crontab)
// now := time.Now()
// for i := 0; i <= 15; i++ {
// if cron.TimeUp() {
// log.Println(time.Since(now))
// now = time.Now()
// }
// time.Sleep(time.Second)
// }
// PrintMemUsage()
}
func TestCrontabPlus(t *testing.T) {
// crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围
// crondata := NewCrontab("*22 * * * *")

View File

@ -1,6 +1,12 @@
package curl2info
import "container/heap"
import (
"container/heap"
"log"
"strings"
"github.com/davecgh/go-spew/spew"
)
// trieWord Trie 需要的Word接口
type trieWord interface {
@ -242,3 +248,234 @@ func (pqe *pQueueExecute) Len() int {
// }
// return content
// }
// CNode 循环链表 三色标记 不确定是否会清除循环引用, 网上说会
type CNode struct {
value interface{}
prev *CNode
next *CNode
}
// GetValue 获取到Node的值
func (node *CNode) GetValue() interface{} {
return node.value
}
// SetValue 获取到Node的值
func (node *CNode) SetValue(value interface{}) {
node.value = value
}
// CircularLinked 循环链表
type CircularLinked struct {
cursor *CNode
head *CNode
tail *CNode
size uint64
}
// NewCircularLinked create a CircularLinked
func NewCircularLinked(values ...interface{}) *CircularLinked {
list := &CircularLinked{}
if len(values) > 0 {
list.Append(values...)
}
return list
}
// Cursor get current Cursor
func (list *CircularLinked) Cursor() *CNode {
if list.cursor == nil {
list.cursor = list.head
}
return list.cursor
}
// MoveNext get next Cursor
func (list *CircularLinked) MoveNext() *CNode {
if list.cursor == nil {
list.cursor = list.head
}
list.cursor = list.cursor.next
return list.cursor
}
// MovePrev get prev Cursor
func (list *CircularLinked) MovePrev() *CNode {
if list.cursor == nil {
list.cursor = list.head
}
list.cursor = list.cursor.prev
return list.cursor
}
// CursorToHead cursor move to head
func (list *CircularLinked) CursorToHead() *CNode {
list.cursor = list.head
return list.cursor
}
// CursorToTail cursor move to tail
func (list *CircularLinked) CursorToTail() *CNode {
list.cursor = list.tail
return list.cursor
}
// GetLoopValues 获取从头到尾的值
func (list *CircularLinked) GetLoopValues() []*CNode {
var result []*CNode
if list.head != nil {
result = append(result, list.head)
for cur := list.head.next; cur != list.head; cur = cur.next {
result = append(result, cur)
}
}
return result
}
// Append a value (one or more) at the end of the list (same as Append())
func (list *CircularLinked) Append(values ...interface{}) {
for _, value := range values {
node := &CNode{value: value}
if list.size == 0 {
list.head = node
list.tail = node
node.next = node
node.prev = node
} else {
list.tail.next = node
node.next = list.head
node.prev = list.tail
list.tail = node
}
list.size++
}
}
// Remove 移除一些节点
func (list *CircularLinked) Remove(node *CNode) {
switch list.size {
case 0:
list.errorNotInList(node)
case 1:
if list.head == node {
list.head = nil
list.tail = nil
node.next = nil
node.prev = nil
list.cursor = nil
list.size--
} else {
list.errorNotInList(node)
}
case 2:
node.prev = nil
node.next = nil
switch node {
case list.head:
list.head = list.tail
list.tail.prev = list.head
list.head.next = list.tail
list.cursor = list.head
list.size--
case list.tail:
list.tail = list.head
list.tail.prev = list.head
list.head.next = list.tail
list.cursor = list.head
list.size--
default:
list.errorNotInList(node)
}
default:
switch node {
case list.head:
_, next := list.cutAndSplice(node)
list.size--
list.head = next
if list.cursor == node {
list.cursor = next
}
case list.tail:
prev, _ := list.cutAndSplice(node)
list.size--
list.tail = prev
if list.cursor == node {
list.cursor = prev
}
default:
_, next := list.cutAndSplice(node)
list.size--
if list.cursor == node {
list.cursor = next
}
}
}
}
// LookCursor for list show
func (list *CircularLinked) LookCursor() string {
cursor := list.Cursor()
content := "->["
cur := list.head
if list.size != 0 {
for size := uint64(0); size < list.size; size++ {
if cursor == cur {
content += "(" + spew.Sprint(cur.value) + ")" + ", "
} else {
content += spew.Sprint(cur.value) + ", "
}
cur = cur.next
}
}
content = strings.TrimRight(content, ", ")
showlen := len(content)
if showlen >= 64 {
showlen = 64
}
content += "]" + content[0:showlen] + " ..."
return content
}
// Clear for list show
func (list *CircularLinked) Clear() {
if list.size != 0 {
list.head.prev = nil
list.tail.next = nil
list.head = nil
list.tail = nil
list.cursor = nil
list.size = 0
}
}
// Size for list show
func (list *CircularLinked) Size() uint64 {
return list.size
}
func (list *CircularLinked) errorNotInList(node *CNode) {
log.Println("the node value ", spew.Sprint(node), " is not in list")
}
// cutAndSplice 不考虑边界情况 上层使用的是否判断
func (list *CircularLinked) cutAndSplice(node *CNode) (prev, next *CNode) {
prev = node.prev
next = node.next
prev.next = next
next.prev = prev
node.prev = nil
node.next = nil
return prev, next
}