This is a creation in Article, where the information may have evolved or changed.
Golang in Timer
The Golang provides 2 timer timers and ticker (which should be known if JS is familiar), namely one-time timers and repetitive task timers.
General usage:
func main() { input := make(chan interface{}) //producer - produce the messages go func() { for i := 0; i < 5; i++ { input <- i } input <- "hello, world" }() t1 := time.NewTimer(time.Second * 5) t2 := time.NewTimer(time.Second * 10) for { select { //consumer - consume the messages case msg := <-input: fmt.Println(msg) case <-t1.C: println("5s timer") t1.Reset(time.Second * 5) case <-t2.C: println("10s timer") t2.Reset(time.Second * 10) } }}
SOURCE Watch
This c is what, we go to the source to see, take the timer as an example:
type Timer struct {C <-chan Timer runtimeTimer}
The original is a channel, actually have to go basic know, go operator when appear, or <-, must have one end refers to channel. In the example above, it is blocking in a for loop, waiting for the C of the timer to come out of the channel, when the value is obtained, the desired operation.
Design our scheduled Task queue
My needs
At that time, I needed to receive a request from the client and generate a timed task that would be executed at a fixed time, either once or several times, or it might be stopped automatically at the specified time, and I would be able to stop when the task was terminated.
Specifically I drew a flowchart, almost as follows, drawing level is limited, please forgive me.
Defining structures
type OnceCron struct {tasks []*Task //任务的列队add chan *Task //当遭遇到新任务的时候remove chan string //当遭遇到删除任务的时候stop chan struct{} //当遇到停止信号的时候Logger *log.Logger //日志 }type Job interface {Run() //执行接口}type Task struct { Job Job //要执行的任务 Uuid string //任务标识,删除时用RunTime int64 //执行时间Spacing int64 //间隔时间EndTime int64 //结束时间Number int //总共要次数}
Queue implementation
First, we're going to get a queue task
Func Newcron () *oncecron general operation, in order to save space, I do not write out, specifically can see the source, posted on the bottom.
Then, the start of the timer queue run, in general, will be named start. Then there is a problem, when we first started the program, this time there is no task queue, it is not for{select{}} Waiting for a fluffy ball? So, we need to add a default task at start, and I did that by adding a repeating queue that executes once an hour to prevent the queue from exiting.
func (one *OnceCron) Start() {//初始化的時候加入一個一年的長定時器,間隔1小時執行一次task := getTaskWithFuncSpacing(3600, time.Now().Add(time.Hour*24*365).Unix() , func() {log.Println("It's a Hour timer!")}) //为了代码格式markdown 里面有个括号我改成全角了one.tasks = append(one.tasks, task)go one.run() //协成执行 防止主进程被阻塞}
The executive section should be focused, and my understanding is that it is divided into three parts:
- First, get a task that performs first
- Then a timer is generated to perform the task
- Make blocking judgments, get the action we want to take
func (one *OnceCron) run() {for { //第一步 获取任务now := time.Now() //获取到当前时间task, key := one.GetTask() //获取最近的一个任务的执行时间i64 := task.RunTime - now.Unix() //任务执行和当前时间的差var d time.Durationif i64 < 0 { //如果任务时间已过期,将执行时间改成现在并且利马执行one.tasks[key].RunTime = now.Unix() one.doAndReset(key) continue} else { //否则,获取距离执行开始的间隔时间d = time.Unix(task.RunTime, 0).Sub(now)} //第二步 产生定时器timer := time.NewTimer(d) //第三步 捕获定时器或者其他事件for {select { //当定时器到了执行时间时,执行当前任务并关闭定时器case <-timer.C:one.doAndReset(key)if task != nil {go task.Job.Run()timer.Stop()}//当外部添加了任务时,关闭当前定时器case <-one.add:timer.Stop()//当外部要删除一个任务时,删除ID为uuidstr的任务case uuidstr := <-one.remove:one.removeTask(uuidstr)timer.Stop()//当遇到要关闭整个定时器任务时case <-one.stop:timer.Stop()return}break}}}
Postscript
This article is purely an article of the note Analysis class, designed to analyze how I encountered a need to generate the code that we needed by analyzing the process.
Source Address:
Timing a task queue
Application Address:
A forwarding middleware applied to Google message push
Reference Source:
Golang implementation of Crontab functions