国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Golang > 正文

用golang實現(xiàn)一個定時器任務隊列實例

2020-04-01 18:57:08
字體:
來源:轉載
供稿:網(wǎng)友

很有幸得到公司信任,采用新的語言進行一些底層服務的開發(fā),在實現(xiàn)功能的同時,也獲得了一些感悟,因此在這記錄一下,方便自己查看也可以共享給大家。

golang定時器

golang中提供了2種定時器timer和ticker(如果JS很熟悉的話應該會很了解),分別是一次性定時器和重復任務定時器。

一般用法:

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)  } }}

源碼觀察

這個C是啥,我們?nèi)ピ创a看看,以timer為例:

type Timer struct { C <-chan Time r runtimeTimer}

原來是一個channel,其實有GO基礎的都知道,GO的運算符當出現(xiàn)的->或者<-的時候,必然是有一端是指channel。按照上面的例子來看,就是阻塞在一個for循環(huán)內(nèi),等待到了定時器的C從channel出來,當獲取到值的時候,進行想要的操作。

設計我們的定時任務隊列

我的需求

當時我的需求是這樣,我需要接收到客戶端的請求并產(chǎn)生一個定時任務,會在固定時間執(zhí)行,可能是一次,也可能是多次,也可能到指定時間自動停止,可能當任務終止的時候,我還要能停止掉。

具體我畫了個流程圖,差不多如下,畫圖水平有限,請見諒。

golang,定時器,任務隊列

定義結構

type OnceCron struct { tasks []*Task   //任務的列隊 add chan *Task  //當遭遇到新任務的時候 remove chan string  //當遭遇到刪除任務的時候 stop chan struct{}  //當遇到停止信號的時候 Logger *log.Logger  //日志 }type Job interface { Run()     //執(zhí)行接口}type Task struct {  Job  Job   //要執(zhí)行的任務  Uuid string   //任務標識,刪除時用 RunTime int64   //執(zhí)行時間 Spacing int64   //間隔時間 EndTime int64   //結束時間 Number int    //總共要次數(shù)}

隊列實現(xiàn)

首先,我們要獲得一個隊列任務

func NewCron() *OnceCron 常規(guī)操作,為了節(jié)省篇幅,我就不寫出來,具體可以看源碼,貼在了底部。

然后,開始定時器隊列的運行,一般,都會命名為Start。那么就有一個問題,我們剛開始啟動程序的時候,這個時候是沒有任務隊列,那豈不是for{ select{}}在等待個毛毛球?所以,我們需要在Start的時候添加一個默認的任務, 我是這么做的,添加了一個一小時執(zhí)行一次的重復隊列,防止隊列退出。

func (one *OnceCron) Start() { //初始化的時候加入一個一年的長定時器,間隔1小時執(zhí)行一次 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() //協(xié)成執(zhí)行 防止主進程被阻塞}

執(zhí)行部分應該是重點的,我的理解是,分成三部:

  1. 首先獲得一個最先執(zhí)行的任務
  2. 然后產(chǎn)生一個定時器,用于執(zhí)行任務
  3. 進行阻塞判斷,獲取我們要進行的操作
func (one *OnceCron) run() { for {    //第一步 獲取任務 now := time.Now() //獲取到當前時間 task, key := one.GetTask() //獲取最近的一個任務的執(zhí)行時間 i64 := task.RunTime - now.Unix() //任務執(zhí)行和當前時間的差 var d time.Duration if i64 < 0 { //如果任務時間已過期,將執(zhí)行時間改成現(xiàn)在并且利馬執(zhí)行 one.tasks[key].RunTime = now.Unix()  one.doAndReset(key)      continue } else { //否則,獲取距離執(zhí)行開始的間隔時間 d = time.Unix(task.RunTime, 0).Sub(now) }    //第二步 產(chǎn)生定時器 timer := time.NewTimer(d)  //第三步 捕獲定時器或者其他事件 for { select {       //當定時器到了執(zhí)行時間時,執(zhí)行當前任務并關閉定時器 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 } }}

后記

這個文章純粹為筆記分析類的文章,旨在分析我碰到一個需求是如何通過分析過程來產(chǎn)生我們需要的代碼的。

源碼地址:timing 一個任務隊列

應用地址:一個應用于谷歌消息推送的轉發(fā)中間件

參考源碼:GOLANG實現(xiàn)crontab功能 

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網(wǎng)。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 克东县| 荣昌县| 南宫市| 蓬安县| 张家川| 铜陵市| 页游| 呼伦贝尔市| 杭锦后旗| 乌兰察布市| 达州市| 屏山县| 平度市| 多伦县| 珠海市| 盐山县| 江孜县| 襄垣县| 彭州市| 吴旗县| 闽清县| 阿拉善左旗| 贵德县| 长顺县| 兴山县| 邵阳县| 滁州市| 潢川县| 卢龙县| 东辽县| 郴州市| 泰宁县| 乐平市| 丰镇市| 栾城县| 庐江县| 剑阁县| 临猗县| 雷州市| 湖北省| 疏勒县|