cron with recover
WithChain
Job 包装器可以在执行实际的Job前后添加一些逻辑:
- 捕获panic;
- 如果Job上次运行还未结束,推迟本次执行;
- 如果Job上次运行还未介绍,跳过本次执行;
- 记录每个Job的执行情况。
可以将Chain类比为 Web 处理器的中间件。实际上就是在Job的执行逻辑外在封装一层逻辑。我们的封装逻辑需要写成一个函数,传入一个Job类型,返回封装后的Job。cron为这种函数定义了一个类型JobWrapper:
// chain.go
type JobWrapper func(Job) Job然后使用一个Chain对象将这些JobWrapper组合到一起:
type Chain struct {
  wrappers []JobWrapper
}
func NewChain(c ...JobWrapper) Chain {
  return Chain{c}
}调用Chain对象的Then(job)方法应用这些JobWrapper,返回最终的`Job:
// Then decorates the given job with all JobWrappers in the chain.
//
// This:
//     NewChain(m1, m2, m3).Then(job)
// is equivalent to:
//     m1(m2(m3(job)))
func (c Chain) Then(j Job) Job {
	for i := range c.wrappers {
		j = c.wrappers[len(c.wrappers)-i-1](j)
	}
	return j
}注意应用JobWrapper的顺序。内置JobWrapper
cron内置了 3 个用得比较多的JobWrapper:
- Recover:捕获内部- Job产生的 panic;
- DelayIfStillRunning:触发时,如果上一次任务还未执行完成(耗时太长),则等待上一次任务完成之后再执行;
- SkipIfStillRunning:触发时,如果上一次任务还未完成,则跳过此次执行。
目前一般都自己实现一个Recover job
比如:
func CronRecover(j cron.Job) cron.Job {
	return cron.FuncJob(func() {
		defer func() {
			if r := recover(); r != nil {
				log.Errorf("panic in job on: %v, stack: %s", r, string(debug.Stack()))
			}
		}()
		j.Run()
	})
}
package main
import (
    "fmt"
    "github.com/robfig/cron/v3"
    "runtime"
)
func Stack() []byte {
    buf := make([]byte, 1024)
    for {
        n := runtime.Stack(buf, false)
        if n < len(buf) {
            return buf[:n]
        }
        buf = make([]byte, 2*len(buf))
    }
}
func CronRecover(j cron.Job) cron.Job {
    return cron.FuncJob(func() {
        defer func() {
            if r := recover(); r != nil {
                fmt.Printf("panic in job on: %v, stack: %s", r, string(Stack()))
            }
        }()
        fmt.Printf("Executing recover---------\n")
        j.Run()
    })
}
func list(j cron.Job) cron.Job {
    return cron.FuncJob(func() {
        fmt.Printf("Executing func list \n")
        j.Run()
    })
}
func func1() {
    fmt.Printf("Executing func1 for job ID \n")
}
func func2() {
    fmt.Printf("Executing func2 for job ID \n")
}
func main() {
    c := cron.New(cron.WithChain(CronRecover,list))
    _, _ = c.AddFunc("@every 5s", func1)
    _, _ = c.AddFunc("@every 5s", func2)
    c.Start()
    select {}
}结果是:
Executing recover---------
Executing func list 
Executing func2 for job ID 
Executing recover---------
Executing func list 
Executing func1 for job ID 
Executing recover---------
Executing func list 
Executing func2 for job ID 
Executing recover---------
Executing func list 
Executing func1 for job ID 
^Csignal: interrupt目前cron自带的recover 调用方式为:
type panicJob struct {
  count int
}
func (p *panicJob) Run() {
  p.count++
  if p.count == 1 {
    panic("oooooooooooooops!!!")
  }
  fmt.Println("hello world")
}
func main() {
  c := cron.New()
  c.AddJob("@every 1s", cron.NewChain(cron.Recover(cron.DefaultLogger)).Then(&panicJob{}))
  c.Start()
  time.Sleep(5 * time.Second)
}chain.go
// Recover panics in wrapped jobs and log them with the provided logger.
func Recover(logger Logger) JobWrapper {
	return func(j Job) Job {
		return FuncJob(func() {
			defer func() {
				if r := recover(); r != nil {
					const size = 64 << 10
					buf := make([]byte, size)
					buf = buf[:runtime.Stack(buf, false)]
					err, ok := r.(error)
					if !ok {
						err = fmt.Errorf("%v", r)
					}
					logger.Error(err, "panic", "stack", "...\n"+string(buf))
				}
			}()
			j.Run()
		})
	}
}
Job接口
除了直接将无参函数作为回调外,cron还支持Job接口:
| 
 |  | 
我们定义一个实现接口Job的结构:
| 
 |  | 
调用cron对象的AddJob()方法将GreetingJob对象添加到定时管理器中:
| 
 |  | 
运行效果:
| 
 |  | 
使用自定义的结构可以让任务携带状态(Name字段)。
实际上AddFunc()方法内部也调用了AddJob()方法。首先,cron基于func()类型定义一个新的类型FuncJob:
| 
 |  | 
然后让FuncJob实现Job接口:
| 
 |  | 
在AddFunc()方法中,将传入的回调转为FuncJob类型,然后调用AddJob()方法:
| 
 |  | 
    http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号