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的胖子