版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第Golang分布式應(yīng)用定時(shí)任務(wù)示例詳解目錄正文最小堆時(shí)間輪總結(jié)
正文
在系統(tǒng)開發(fā)中,有一類任務(wù)不是立即執(zhí)行,而是在未來某個(gè)時(shí)間點(diǎn)或者按照一定間隔去執(zhí)行,比如日志定期壓縮、報(bào)表制作、過期數(shù)據(jù)清理等,這就是定時(shí)任務(wù)。
在單機(jī)中,定時(shí)任務(wù)通常需要實(shí)現(xiàn)一個(gè)類似crontab的系統(tǒng),一般有兩種方式:
最小堆,按照任務(wù)執(zhí)行時(shí)間建堆,每次取最近的任務(wù)執(zhí)行時(shí)間輪,將任務(wù)放到時(shí)間輪列表中,每次轉(zhuǎn)動(dòng)取對(duì)應(yīng)的任務(wù)列表執(zhí)行
最小堆
最小堆是一種特殊的完全二叉樹,任意非葉子節(jié)點(diǎn)的值不大于其子節(jié)點(diǎn),如圖
通過最小堆,根據(jù)任務(wù)最近執(zhí)行時(shí)間鍵堆,每次取堆頂元素即最近需要執(zhí)行的任務(wù),設(shè)置timer定時(shí)器,到期后觸發(fā)任務(wù)執(zhí)行。由于堆的特性每次調(diào)整的時(shí)間復(fù)雜度為O(lgN),相較于普通隊(duì)列性能更快。
在container/heap中已經(jīng)實(shí)現(xiàn)操作堆的相關(guān)函數(shù),我們只需要實(shí)現(xiàn)定期任務(wù)核心邏輯即可。
//運(yùn)行
func(c*Cron)Run()error{
//設(shè)置cron已啟動(dòng),atomic.Bool來保證并發(fā)安全
c.started.Store(true)
//主循環(huán)
for{
//如果停止則退出
if!c.started.Load(){
break
c.runTask()
returnnil
//核心邏輯
func(c*Cron)runTask(){
now:=time.Now()
duration:=infTime
//獲取堆頂元素
task,ok:=c.tasks.Peek()
ifok{
//如果已刪除則彈出
if!c.set.Has(task.Name()){
c.tasks.Pop()
return
//計(jì)算于當(dāng)前時(shí)間查找,設(shè)置定時(shí)器
iftask.next.After(now){
duration=task.next.Sub(now)
}else{
duration=0
timer:=time.NewTimer(duration)
defertimer.Stop()
//當(dāng)有新元素插入直接返回,防止新元素執(zhí)行時(shí)間小于當(dāng)前堆頂元素
select{
case-c.new:
return
case-timer.C:
//彈出任務(wù),執(zhí)行
gotask.Exec()
//計(jì)算下次執(zhí)行時(shí)間,如果為0說明任務(wù)已結(jié)束,否則重新入堆
task.next=task.Next(time.Now())
iftask.next.IsZero(){
c.set.Delete(task.Name())
}else{
c.tasks.Push(task)
主要邏輯可總結(jié)為:
將任務(wù)按照下次執(zhí)行時(shí)間建最小堆每次取堆頂任務(wù),設(shè)置定時(shí)器如果中間有新加入任務(wù),轉(zhuǎn)入步驟2定時(shí)器到期后執(zhí)行任務(wù)再次取下個(gè)任務(wù),轉(zhuǎn)入步驟2,依次執(zhí)行
時(shí)間輪
另一種實(shí)現(xiàn)Cron的方式是時(shí)間輪,時(shí)間輪通過一個(gè)環(huán)形隊(duì)列,每個(gè)插槽放入需要到期執(zhí)行的任務(wù),按照固定間隔轉(zhuǎn)動(dòng)時(shí)間輪,取插槽中任務(wù)列表執(zhí)行,如圖所示:
時(shí)間輪可看作一個(gè)表盤,如圖中時(shí)間間隔為1秒,總共60個(gè)格子,如果任務(wù)在3秒后執(zhí)行則放為插槽3,每秒轉(zhuǎn)動(dòng)次取插槽上所有任務(wù)執(zhí)行。
如果執(zhí)行時(shí)間超過最大插槽,比如有個(gè)任務(wù)需要63秒后執(zhí)行(超過了最大格子刻度),一般可以通過多層時(shí)間輪,或者設(shè)置一個(gè)額外變量圈數(shù),只執(zhí)行圈數(shù)為0的任務(wù)。
時(shí)間輪插入的時(shí)間復(fù)雜度為O(1),獲取任務(wù)列表復(fù)雜度為O(1),執(zhí)行列表最差為O(n)。對(duì)比最小堆,時(shí)間輪插入刪除元素更快。
核心代碼如下:
//定義
typeTimeWheelstruct{
intervaltime.Duration//觸發(fā)間隔
slotsint//總插槽數(shù)
currentSlotint//當(dāng)前插槽數(shù)
tasks[]*list.List//環(huán)形列表,每個(gè)元素為對(duì)應(yīng)插槽的任務(wù)列表
setcontainerx.Set[string]//記錄所有任務(wù)key值,用來檢查任務(wù)是否被刪除
tricker*time.Ticker//定時(shí)觸發(fā)器
loggerlogr.Logger
func(tw*TimeWheel)Run()error{
tw.tricker=time.NewTicker(erval)
for{
//通過定時(shí)器模擬時(shí)間輪轉(zhuǎn)動(dòng)
now,ok:=-tw.tricker.C
if!ok{
break
//轉(zhuǎn)動(dòng)一次,執(zhí)行任務(wù)列表
tw.RunTask(now,tw.currentSlot)
tw.currentSlot=(tw.currentSlot+1)%tw.slots
returnnil
func(tw*TimeWheel)RunTask(nowtime.Time,slotint){
//一次執(zhí)行任務(wù)列表
foritem:=taskList.Front();item!=nil;{
task,ok:=item.Value.(*TimeWheelTask)
//任務(wù)圈數(shù)大于0,不需要執(zhí)行,將圈數(shù)減一
iftask.circle0{
task.circle--
item=item.Next()
continue
//運(yùn)行任務(wù)
gotask.Exec()
//計(jì)算任務(wù)下次運(yùn)行時(shí)間
next:=item.Next()
taskList.Remove(item)
item=next
task.next=task.Next(now)
if!task.next.IsZero(){
tw.add(now,task)
}else{
tw.Remove(task.Name())
//添加任務(wù),計(jì)算下一次任務(wù)執(zhí)行的插槽與圈數(shù)
func(tw*TimeWheel)add(nowtime.Time,task*TimeWheelTask){
if!task.initialized{
task.next=task.Next(now)
task.initialized=true
duration:=task.next.Sub(now)
ifduration=0{
task.slot=tw.currentSlot+1
task.circle=0
}else{
mult:=int(duration/erval)
task.slot=(tw.currentSlot+mult)%tw.slots
task.circle=mult/tw.slots
tw.tasks[task.slot].PushBack(task)
tw.set.Insert(task.Name())
時(shí)間輪的主要邏輯如下:
將任務(wù)存在對(duì)應(yīng)插槽的時(shí)間通過定時(shí)間模擬時(shí)間輪轉(zhuǎn)動(dòng)每次到期后遍歷當(dāng)前插槽的任務(wù)列表,若任務(wù)圈數(shù)為0則執(zhí)行如果任務(wù)未結(jié)束,計(jì)算下次執(zhí)行的插槽與圈數(shù)轉(zhuǎn)入步驟2,依次執(zhí)行
總結(jié)
本文主要總結(jié)了定時(shí)任務(wù)的兩種實(shí)現(xiàn)方式,最小堆與時(shí)間輪,并分析其核心實(shí)現(xiàn)邏輯。
對(duì)于執(zhí)行分布式定時(shí)任務(wù),可以借助延時(shí)消息隊(duì)列或者直接使用Kubernetes的CronJob。
自己開發(fā)的話可以借助Etcd:
中心節(jié)點(diǎn)Coordinator將任務(wù)按照一定算
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026外貿(mào)業(yè)務(wù)員考試國際商法國際慣例應(yīng)用題集
- 2026年投資理財(cái)知識(shí)學(xué)習(xí)試題
- 2026年經(jīng)濟(jì)學(xué)疑難案例解析與處罰分析
- 軟件開發(fā)流程規(guī)范與標(biāo)準(zhǔn)(標(biāo)準(zhǔn)版)
- 汽車銷售與服務(wù)操作手冊(cè)(標(biāo)準(zhǔn)版)
- 未來五年塑料角閥市場(chǎng)需求變化趨勢(shì)與商業(yè)創(chuàng)新機(jī)遇分析研究報(bào)告
- 遵義市播州區(qū)2025年網(wǎng)格員考試試題及答案
- 鄭州市二七區(qū)2025年網(wǎng)格員考試試題及答案
- 未來五年外國快餐行業(yè)市場(chǎng)營銷創(chuàng)新戰(zhàn)略制定與實(shí)施分析研究報(bào)告
- 未來五年寵物美容服務(wù)企業(yè)數(shù)字化轉(zhuǎn)型與智慧升級(jí)戰(zhàn)略分析研究報(bào)告
- 任務(wù)汽車的自救與互救教學(xué)要求解釋車輛自救互救的基本概念
- 大學(xué)之道故事解讀
- GB/T 18851.2-2024無損檢測(cè)滲透檢測(cè)第2部分:滲透材料的檢驗(yàn)
- 洗滌設(shè)備售后服務(wù)標(biāo)準(zhǔn)化方案
- 電力設(shè)施管溝開挖安全操作方案
- 中藥材精加工合作合同
- 2023年全國職業(yè)院校技能大賽-生產(chǎn)事故應(yīng)急救援賽項(xiàng)規(guī)程
- 學(xué)校零星維護(hù)維修方案
- 網(wǎng)站對(duì)歷史發(fā)布信息進(jìn)行備份和查閱的相關(guān)管理制度及執(zhí)行情況說明(模板)
- NB-T 47013.1-2015 承壓設(shè)備無損檢測(cè) 第1部分-通用要求
- 廣東廣州市黃埔區(qū)統(tǒng)計(jì)局招考聘用市商業(yè)調(diào)查隊(duì)隊(duì)員參考題庫+答案詳解
評(píng)論
0/150
提交評(píng)論