佳木斯湛栽影视文化发展公司

主頁(yè) > 知識(shí)庫(kù) > 用golang實(shí)現(xiàn)一個(gè)定時(shí)器任務(wù)隊(duì)列實(shí)例

用golang實(shí)現(xiàn)一個(gè)定時(shí)器任務(wù)隊(duì)列實(shí)例

熱門標(biāo)簽:百度競(jìng)價(jià)排名 服務(wù)外包 地方門戶網(wǎng)站 Linux服務(wù)器 AI電銷 鐵路電話系統(tǒng) 網(wǎng)站排名優(yōu)化 呼叫中心市場(chǎng)需求

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

golang中定時(shí)器

golang中提供了2種定時(shí)器timer和ticker(如果JS很熟悉的話應(yīng)該會(huì)很了解),分別是一次性定時(shí)器和重復(fù)任務(wù)定時(shí)器。

一般用法:

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

源碼觀察

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

type Timer struct {
 C -chan Time
 r runtimeTimer
}

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

設(shè)計(jì)我們的定時(shí)任務(wù)隊(duì)列

我的需求

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

具體我畫了個(gè)流程圖,差不多如下,畫圖水平有限,請(qǐng)見諒。

定義結(jié)構(gòu)

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

隊(duì)列實(shí)現(xiàn)

首先,我們要獲得一個(gè)隊(duì)列任務(wù)

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

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

func (one *OnceCron) Start() {
 //初始化的時(shí)候加入一個(gè)一年的長(zhǎng)定時(shí)器,間隔1小時(shí)執(zhí)行一次
 task := getTaskWithFuncSpacing(3600, time.Now().Add(time.Hour*24*365).Unix() , func() {
 log.Println("It's a Hour timer!")
 }) //為了代碼格式markdown 里面有個(gè)括號(hào)我改成全角了
 one.tasks = append(one.tasks, task)
 go one.run() //協(xié)成執(zhí)行 防止主進(jìn)程被阻塞
}

執(zhí)行部分應(yīng)該是重點(diǎn)的,我的理解是,分成三部:

  1. 首先獲得一個(gè)最先執(zhí)行的任務(wù)
  2. 然后產(chǎn)生一個(gè)定時(shí)器,用于執(zhí)行任務(wù)
  3. 進(jìn)行阻塞判斷,獲取我們要進(jìn)行的操作
func (one *OnceCron) run() {

 for {
    //第一步 獲取任務(wù)
 now := time.Now() //獲取到當(dāng)前時(shí)間
 task, key := one.GetTask() //獲取最近的一個(gè)任務(wù)的執(zhí)行時(shí)間
 i64 := task.RunTime - now.Unix() //任務(wù)執(zhí)行和當(dāng)前時(shí)間的差

 var d time.Duration
 if i64  0 { //如果任務(wù)時(shí)間已過期,將執(zhí)行時(shí)間改成現(xiàn)在并且利馬執(zhí)行
 one.tasks[key].RunTime = now.Unix() 
 one.doAndReset(key)
      continue
 } else { //否則,獲取距離執(zhí)行開始的間隔時(shí)間
 d = time.Unix(task.RunTime, 0).Sub(now)
 }
    //第二步 產(chǎn)生定時(shí)器
 timer := time.NewTimer(d) 

 //第三步 捕獲定時(shí)器或者其他事件
 for {
 select { 
      //當(dāng)定時(shí)器到了執(zhí)行時(shí)間時(shí),執(zhí)行當(dāng)前任務(wù)并關(guān)閉定時(shí)器
 case -timer.C:
 one.doAndReset(key)
 if task != nil {
  go task.Job.Run()
  timer.Stop()
 }

 //當(dāng)外部添加了任務(wù)時(shí),關(guān)閉當(dāng)前定時(shí)器
 case -one.add:
 timer.Stop()
 //當(dāng)外部要?jiǎng)h除一個(gè)任務(wù)時(shí),刪除ID為uuidstr的任務(wù)
 case uuidstr := -one.remove:
 one.removeTask(uuidstr)
 timer.Stop()
 //當(dāng)遇到要關(guān)閉整個(gè)定時(shí)器任務(wù)時(shí)
 case -one.stop:
 timer.Stop()
 return
 }

 break
 }
 }
}

后記

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

源碼地址:timing 一個(gè)任務(wù)隊(duì)列

應(yīng)用地址:一個(gè)應(yīng)用于谷歌消息推送的轉(zhuǎn)發(fā)中間件

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

您可能感興趣的文章:
  • Golang定時(shí)器的2種實(shí)現(xiàn)方法與區(qū)別
  • golang定時(shí)器和超時(shí)的使用詳解
  • Golang 定時(shí)器(Timer 和 Ticker),這篇文章就夠了
  • Golang中定時(shí)器的陷阱詳解
  • golang中定時(shí)器cpu使用率高的現(xiàn)象詳析
  • golang time包下定時(shí)器的實(shí)現(xiàn)方法
  • Golang 定時(shí)器的終止與重置實(shí)現(xiàn)

標(biāo)簽:銅川 黃山 仙桃 湖南 蘭州 湘潭 衡水 崇左

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《用golang實(shí)現(xiàn)一個(gè)定時(shí)器任務(wù)隊(duì)列實(shí)例》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    句容市| 哈巴河县| 新竹市| 县级市| 喀什市| 漯河市| 阳西县| 沐川县| 芦山县| 云阳县| 南溪县| 大余县| 大安市| 准格尔旗| 哈尔滨市| 奉贤区| 柏乡县| 叙永县| 海安县| 通道| 乐陵市| 仙居县| 西贡区| 辽阳县| 永顺县| 河间市| 镇原县| 巴马| 景谷| 阳东县| 白朗县| 桂东县| 渑池县| 蒙城县| 凌海市| 库车县| 东明县| 霍山县| 西乡县| 建昌县| 台北市|