<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在系統開發中,有一類任務不是立即執行,而是在未來某個時間點或者按照一定間隔去執行,比如紀錄檔定期壓縮、報表製作、過期資料清理等,這就是定時任務。
在單機中,定時任務通常需要實現一個類似crontab的系統,一般有兩種方式:
最小堆是一種特殊的完全二元樹,任意非葉子節點的值不大於其子節點,如圖
通過最小堆,根據任務最近執行時間鍵堆,每次取堆頂元素即最近需要執行的任務,設定timer定時器,到期後觸發任務執行。由於堆的特性每次調整的時間複雜度為O(lgN),相較於普通佇列效能更快。
在container/heap
中已經實現操作堆的相關函數,我們只需要實現定期任務核心邏輯即可。
// 執行 func (c *Cron) Run() error { // 設定cron已啟動,atomic.Bool來保證並行安全 c.started.Store(true) // 主迴圈 for { // 如果停止則退出 if !c.started.Load() { break } c.runTask() } return nil } // 核心邏輯 func (c *Cron) runTask() { now := time.Now() duration := infTime // 獲取堆頂元素 task, ok := c.tasks.Peek() if ok { // 如果已刪除則彈出 if !c.set.Has(task.Name()) { c.tasks.Pop() return } // 計算於當前時間查詢,設定定時器 if task.next.After(now) { duration = task.next.Sub(now) } else { duration = 0 } } timer := time.NewTimer(duration) defer timer.Stop() // 當有新元素插入直接返回,防止新元素執行時間小於當前堆頂元素 select { case <-c.new: return case <-timer.C: } // 彈出任務,執行 go task.Exec() // 計算下次執行時間,如果為0說明任務已結束,否則重新入堆 task.next = task.Next(time.Now()) if task.next.IsZero() { c.set.Delete(task.Name()) } else { c.tasks.Push(task) } }
主要邏輯可總結為:
另一種實現Cron的方式是時間輪,時間輪通過一個環形佇列,每個插槽放入需要到期執行的任務,按照固定間隔轉動時間輪,取插槽中任務列表執行,如圖所示:
時間輪可看作一個錶盤,如圖中時間間隔為1秒,總共60個格子,如果任務在3秒後執行則放為插槽3,每秒轉動次取插槽上所有任務執行。
如果執行時間超過最大插槽,比如有個任務需要63秒後執行(超過了最大格子刻度),一般可以通過多層時間輪,或者設定一個額外變數圈數,只執行圈數為0的任務。
時間輪插入的時間複雜度為O(1),獲取任務列表複雜度為O(1),執行列表最差為O(n)。對比最小堆,時間輪插入刪除元素更快。
核心程式碼如下:
// 定義 type TimeWheel struct { interval time.Duration // 觸發間隔 slots int // 總插槽數 currentSlot int // 當前插槽數 tasks []*list.List // 環形列表,每個元素為對應插槽的任務列表 set containerx.Set[string] // 記錄所有任務key值,用來檢查任務是否被刪除 tricker *time.Ticker // 定時觸發器 logger logr.Logger } func (tw *TimeWheel) Run() error { tw.tricker = time.NewTicker(tw.interval) for { // 通過定時器模擬時間輪轉動 now, ok := <-tw.tricker.C if !ok { break } // 轉動一次,執行任務列表 tw.RunTask(now, tw.currentSlot) tw.currentSlot = (tw.currentSlot + 1) % tw.slots } return nil } func (tw *TimeWheel) RunTask(now time.Time, slot int) { // 一次執行任務列表 for item := taskList.Front(); item != nil; { task, ok := item.Value.(*TimeWheelTask) // 任務圈數大於0,不需要執行,將圈數減一 if task.circle > 0 { task.circle-- item = item.Next() continue } // 執行任務 go task.Exec() // 計算任務下次執行時間 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()) } } } // 新增任務,計算下一次任務執行的插槽與圈數 func (tw *TimeWheel) add(now time.Time, task *TimeWheelTask) { if !task.initialized { task.next = task.Next(now) task.initialized = true } duration := task.next.Sub(now) if duration <= 0 { task.slot = tw.currentSlot + 1 task.circle = 0 } else { mult := int(duration / tw.interval) task.slot = (tw.currentSlot + mult) % tw.slots task.circle = mult / tw.slots } tw.tasks[task.slot].PushBack(task) tw.set.Insert(task.Name()) }
時間輪的主要邏輯如下:
本文主要總結了定時任務的兩種實現方式,最小堆與時間輪,並分析其核心實現邏輯。
對於執行分散式定時任務,可以藉助延時訊息佇列或者直接使用Kubernetes的CronJob。
自己開發的話可以藉助Etcd:
本文所有程式碼見github.com/qingwave/go…
以上就是Golang分散式應用定時任務範例詳解的詳細內容,更多關於Golang分散式定時的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45