runner包简介
- 用途
- 使用场景
- 调度后台处理任务的程序(该任务可能作为cron作业执行,or在基于定时任务的云环境里执行)
源码解析
// runner 包管理处理任务的运行与生命周期
package runner
import (
"errors"
"os"
"os/signal"
"time"
)
// 声明Runner结构,Runner的任务有
//1. 在给定的超时时间内执行一组任务
//2. 在操作系统发送中断信号时结束这些任务
type Runner struct {
// interrupt channel 接收从操作系统发来的信号
interruput chan os.Signal
// complete channel 报告处理任务已完成
complete chan error
// timeout chanel 报告处理任务已超时
timeout <-chan time.Time
// tasks 持有一组以索引顺序执行的任务函数
tasks []func(int)
}
// ErrTimeout 是在任务超时时返回的错误
var ErrTimeout = errors.New("received timeout")
// ErrInterrupt 是在接收到操作系统信号时返回的错误
var ErrInterrupt = errors.New("received interrupt")
// 工厂函数, 返回一个Runner类型的指针
func New(d time.Duration) *Runner {
// 分别初始化channel中的各个通道值
return &Runner{
// 将interrupt 初始化为缓存为1的channel,保证至少能接收一个来自语言运行时的os.Signal,
// 确保语言运行时发送这个事件的时候不会被阻塞,如果goroutine没有准备好接收数据,
// 数据将被抛弃。比如用户在狂按ctrl+C时,程序只有在准备好时接收事件,其余将被抛弃。
interrupt: make(chan os.Signal, 1),
// complete 初始化为一个无缓存的channel, 当执行任务的goroutine完成时,会向这个通道发送一个error值或者nil值
// 一旦 main接收了这个error值, goroutine 就可以安全地终止了
complete: make(chan error),
// time.After()是time包的内置函数,返回一个time.Time类型的channel值
// 语言运行时会在指定的 duration 时间到期之后,向这个channel发一个 time.Time 值。
timeout: time.After(d),
}
}
// 与Runner关联的方法
// Add 将一个任务添加到Runner中,这个任务是一个接收一个int参数,不返回任何参数的函数
func (r *Runner) Add(tasks ...func(int)) {
r.tasks = append(r.tasks, tasks...)
}
// run 执行每一个已注册的任务
func (r *Runner) run() error {
for id, task := range r.tasks {
if r.gotInterrupt() {
return ErrInterrupt
}
// 执行已注册的任务
task(id)
}
return nil
}
}
// gotInterrupt 方法验证是否接受到了中断信号
func (r *Runner) gotInterrupt() bool {
select {
// 接收到中断信号后,停止接收后续任何信号
case <- r.interrupt:
signal.Stop(r.interrrupt)
return true
default:
return false
}
}
// Start 执行所有任务,同时监听channel event
func (r *Runner) Start() error {
// 接收所有中断信号
signal.Notify(r.interrupt, os.Interrupt)
// 将任务放在不同的goroutine中执行
go func() {
r.complete <- r.run()
}()
select {
// 任务正常结束时,发出信号
case err := <-r.complete:
return err
// 任务超时时,发出信号
case <- r.timeout:
return ErrTimeout
}
}