1. 程式人生 > >TimingWheel[時間輪]介紹

TimingWheel[時間輪]介紹

Kafka的延遲操作是一個相對獨立的元件,他的主要功能是管理延遲操作,底層依賴於Kafka提供的時間輪實現。JDK本身提供的java.util.Timer也可以實現定時任務,但是如果系統請求量巨大,效能要求很高,他們底層所依賴的資料結構存取操作複雜度都是O(nlog(n))

為了將時間複雜度降為o(1),一般會使用其他方式的定時任務元件,比如zookeeper的時間桶方式處理session過期,netty也使用Hash

WheelTimer這種時間輪的實現。


Kafka時間輪的實現是TimingWheel,他是一個儲存定時任務的環形佇列(桶),底層使用陣列實現,陣列中每一個元素可以存放一個TimerTaskList物件

TimerTaskList是環形雙向連結串列,在其中連結串列項TimeTaskEntry封裝了真正的定時任務TimerTask。TimerTaskList使用expiration欄位記錄了整個TimerTaskList的超時時間。TimeTaskEntry中的expirationMs欄位記錄了超時時間戳,timerTask欄位指向了對應的TimerTask任務.

TimerTask中的delayMs記錄了任務的延遲時間,timerTaskEntry記錄了TimerTaskEntry物件

TimingWheel提供了分層的概念,因為年時間跨度比較大,數量很大,單層的時間輪會造成任務的round很大,單個格子連結串列很長。一般情況,第一層時間跨度是最小的,第二層時間跨度比較大。

如上圖所示:假設編號為0的時間格或者桶儲存著到期時間為t,每一個tick的持續時間(tickDuration)為20ms,在這個格子裡只能儲存著到期時間為[t~t+20]ms的任務,任務到底放在哪一個時間格或者桶裡面根據不同的場景可以有不同的演算法,假設時間輪的時間格有n個,到期時間為m(ms),那麼計算公式m%n = 所在的時間格或者桶,比如n=10,m=34ms,那麼他所在桶或者時間格是4

當任務到期時間超出了當前時間所表示的時間範圍時,就會嘗試加到上一層時間輪,如下圖所示:

其中第一層時間輪每個時間格是1ms,整個時間輪跨度是20ms,指標當前時間表示的時間是currentTime,則該時間輪跨度為currentTime

~currentTime+20,只有時間在這段範圍內任務才能新增到該層時間輪等待到期。到期時間超出[currentTime~currentTime+20]這個時間範圍的任務會嘗試新增到上級時間輪中,通過逐層向上級嘗試最終找到合適的時間輪層級

整個時間輪表示的時間跨度是不變的,隨著指標的不斷後移,當前時間輪能處理的時間段也在不斷後移,新來的TimerTaskEntry會複用原來的已經到期的TimerTaskList,如下圖所示,第一層時間輪跨度始終為20ms,指標表示的時間在不段後移。當指標指向0是時間格的時候,假設currentTime = 100,指向第三個時間格,此時指標表示的時間為當前時間104ms,整個時間輪表示的時間段是[104~ 124],但是該時間輪的時間跨度依然是20ms。此時間輪中編號為2的時間格表示的時間不再是102~103,而是123~124



假設現在有一個任務在445ms後執行,預設情況下,各個層級的時間輪的時間格個數為20,第一層時間輪每一個時間格跨度為1ms,整個時間輪跨度為20ms,跨度不夠。第二層時間輪每一個時間格跨度為20ms,整個時間輪跨度為400ms,跨度依然不夠,第三層時間輪每一個時間格跨度為400ms,整個時間輪跨度為8000ms,現在跨度夠了,此任務就放在第三層時間輪的第一個時間格對應的TimerTaskList,等待被執行,此TimerTaskList到期時間是400ms,隨著時間的流逝,當此TimerTaskList到期時,距離該任務到期時間還有45ms,不能執行該任務,我們將重新提交到時間輪,此時第一層時間輪跨度依然不夠,不能執行任務,第二層時間輪時間格跨度為20,整個世間輪跨度為400,跨度足夠,放在第三個時間格等待執行,如此往復幾次,高層時間輪最終會慢慢移動到低層時間輪上,最終任務到期執行。

一 重要屬性

buckets : Array.tabulate[TimerTaskList] 型別,其每一個項都對應時間輪中一個時間格,用於儲存TimerTaskList的陣列

tickMs:Long 當前時間輪中一個時間格表示的時間跨度

wheelSize: Int 當前時間輪的大小也就是總的時間格數量

taskCounter:AtomicInteger 各層級時間輪中任務的總數

startMs:Long 當期時間輪的建立時間

queue:DelayQueue[TimerTaskList] 整個層級的時間輪公用一個任務佇列,其元素型別是TimerTaskList

currentTime:時間輪的指標,將整個時間輪劃分為到期部分和未到期部分。在初始化的時候,currentTime被修剪成tickMs的倍數startMs - (startMs % tickMs)

interval:Long 當前時間輪的時間跨度即tickMs * wheelSize,當前時間輪只能處理時間範圍在currentTime~currentTime+tickMs*WheelSize之間的定時任務,超過這個範圍則需要新增任務到上層時間輪

overflowWheel: TimingWheel 上層時間輪的引用

二 核心方法

2.1 addOverflowWheel 主要用於建立上層時間輪

private[this] def addOverflowWheel(): Unit = {
  synchronized {
    if (overflowWheel == null) {
      overflowWheel = new TimingWheel(
        tickMs = interval, // 上層時間輪的時間格跨度等於下一層時間輪的跨度wheelSize = wheelSize,// 大小不變startMs = currentTime,// 初始化當前時間輪的建立時間taskCounter= taskCounter,//時間輪中任務的總數queue
     
)
    }
  }
}

2.2 add 向時間輪中新增定時任務,同時也會檢測新增的任務是否已經到期

def add(timerTaskEntry: TimerTaskEntry): Boolean = {
  // 獲取定時任務的超時時間戳val expiration = timerTaskEntry.expirationMs
  // 如果任務已經被取消if (timerTaskEntry.cancelled) {
    false // 返回新增失敗} else if (expiration < currentTime + tickMs) { // 如果時間指標現在指向的時間+時間格跨度 > 需要新增的定時任務的超時時間戳表示已經到期
    // 舉個例子:currentTime=102,時間格跨度為10ms,那麼假設新增的任務超時時間戳為105 < 102+10
    false // 返回新增失敗} else if (expiration < currentTime + interval) {// 如果時間指標現在指向的時間+當前時間輪跨度 > 需要新增的定時任務的超時時間戳表示沒有到期
    // 然後把當前任務放進迴圈數組裡面
    // 得到任務到期時間戳/時間格跨度的餘數val virtualId = expiration / tickMs
    // 獲取放在哪一個桶裡 (到期時間戳/時間格跨度)%時間輪跨度val bucket = buckets((virtualId % wheelSize.toLong).toInt)
    bucket.add(timerTaskEntry)

    // 設定時間格的到期時間if (bucket.setExpiration(virtualId * tickMs)) {
      /*
       * 整個時間輪表示的跨度是不變的,隨著指標的後移,當前時間輪能夠處理的時間段也在不段後移,新的TimerTaskEntry會複用原來的已經清理過的
       * TimerTaskList,此時你需要重新設定TimerTaskList的到期時間,並將桶重新入隊
       */
      queue.offer(bucket)
    }
    true
  } else { // 如果超出了時間的跨度範圍,則將其新增到上層時間輪來處理if (overflowWheel == null) addOverflowWheel()
    overflowWheel.add(timerTaskEntry)
  }
}

2.3 advanceClock 嘗試推進當前時間輪的指標,同時也會嘗試推進上層時間輪的指標,隨著當前時間輪的不斷推進,上層時間輪指標早晚會被推進成功

def advanceClock(timeMs: Long): Unit = {
  // 嘗試移動指標currentTime
  if (timeMs >= currentTime + tickMs) {
    currentTime = timeMs - (timeMs % tickMs)

    // 嘗試禿頂上層時間輪指標currentTime
    if (overflowWheel != null) overflowWheel.advanceClock(currentTime)
  }
}

相關推薦

TimingWheel[時間]介紹

Kafka的延遲操作是一個相對獨立的元件,他的主要功能是管理延遲操作,底層依賴於Kafka提供的時間輪實現。JDK本身提供的java.util.Timer也可以實現定時任務,但是如果系統請求量巨大,效能要求很高,他們底層所依賴的資料結構存取操作複雜度都是O(nlog(n))

Kafka解惑之時間TimingWheel

Kafka中存在大量的延遲操作,比如延遲生產、延遲拉取以及延遲刪除等。Kafka並沒有使用JDK自帶的Timer或者DelayQueue來實現延遲的功能,而是基於時間輪自定義了一個用於實現延遲功能的定時器(SystemTimer)。JDK的Timer和Delay

時間演算法(TimingWheel)是如何實現的?

前言 我在2. SOFAJRaft原始碼分析—JRaft的定時任務排程器是怎麼做的?這篇文章裡已經講解過時間輪演算法在JRaft中是怎麼應用的,但是我感覺我並沒有講解清楚這個東西,導致看了這篇文章依然和沒看是一樣的,所以我打算重新說透時間輪演算法。 時間輪的應用並非 JRaft 獨有,其應用場景還有很多,在

SylixOS時間變換介紹

流程 out bool timeout 方法 struct 概述 sso .net [TOC] 1、概述 SylixOS中提供了時間變換相關接口,主要用於計算兩個時間點之差以及根據入參時間timespec計算超時時間。 2、接口介紹 2.1 計算兩個時間點之差 SylixO

簡單說說Kafka中的時間算法

而且 超過 lsi alt UNC -c wheel lean num 零、時間輪定義 簡單說說時間輪吧,它是一個高效的延時隊列,或者說定時器。實際上現在網上對於時間輪算法的解釋很多,定義也很全,這裏引用一下朱小廝博客裏出現的定義: 參考下圖,Kafka中的時間輪(Timi

原 薦 簡單說說Kafka中的時間算法

nds type 說明 同學 秒級 高層 那是 忽略 tick 零、時間輪定義 簡單說說時間輪吧,它是一個高效的延時隊列,或者說定時器。實際上現在網上對於時間輪算法的解釋很多,定義也很全,這裏引用一下 朱小廝博客 裏出現的定義: 參考下圖,Kafka中的時間輪(Timi

簡單說說Kafka中的時間演算法

零、時間輪定義 簡單說說時間輪吧,它是一個高效的延時佇列,或者說定時器。實際上現在網上對於時間輪演算法的解釋很多,定義也很全,這裡引用一下朱小廝部落格裡出現的定義: 參考下圖,Kafka中的時間輪(TimingWheel)是一個儲存定時任務的環形佇列,底層採用陣列實現,陣列中的每個元素

原 薦 簡單說說Kafka中的時間演算法

零、時間輪定義 簡單說說時間輪吧,它是一個高效的延時佇列,或者說定時器。實際上現在網上對於時間輪演算法的解釋很多,定義也很全,這裡引用一下 朱小廝部落格 裡出現的定義: 參考下圖,Kafka中的時間輪(TimingWheel)是一個儲存定時任務的環形佇列,底層採用陣列實現

C時間

看完了《linux高效能伺服器程式設計》對裡面的定時器很感興趣。書中提到三種定時器,分別是:基於升序連結串列的定時器,基於時間輪的定時器,基於時間堆的定時器。三種定時器的實現書中均是給了C++程式碼,不過我對C++不太感興趣,雖然現在在做C++開發,因此寫了C版

時間演算法

前言 現實開發中有許多的延遲操作,比如定時清理過期資料等,在JDK中自帶的Timer或者DelayQueue來實現延遲的功能,但很

心跳與超時:高併發高效能的時間超時器

目錄 心跳與超時:高併發高效能的時間輪超時器 引言 JDK 原生提供的超時任務支援 java.util.Timer ScheduledThreadPoolExecutor

【原創】面試時遇到『看門狗』脖子上掛著『時間』,我就問你怕不怕?

Redisson的看門狗和Netty的時間輪,瞭解一下?寫的過程中順便打了一下自己的臉。技術嘛,不就是在不斷打臉的過程中成長起來的嘛。 荒腔走板聊生活 大家好,一週的時間過的飛快,轉眼間又到週末了。 老規矩,還是本號特色,先是荒腔走板的聊聊生活。 上面的圖片是我在一次跑步的過程中拍的,一隻狗子。可以看到圖

(資料科學學習手札90)Python+Kepler.gl輕鬆製作時間播圖

> 本文示例程式碼及資料已上傳至我的`Github`倉庫[https://github.com/CNFeffery/DataScienceStudyNotes](https://github.com/CNFeffery/DataScienceStudyNotes) # 1 簡介   `Kepler.gl`

(資料科學學習手札90)Python+Kepler.gl輕鬆製作時間播地圖

> 本文示例程式碼及資料已上傳至我的`Github`倉庫[https://github.com/CNFeffery/DataScienceStudyNotes](https://github.com/CNFeffery/DataScienceStudyNotes) # 1 簡介   `Kepler.gl`

網路時間協議介紹以及伺服器同步網路時間

# NTP介紹 網路時間協議(英語:Network Time Protocol,縮寫:NTP)是在資料網路潛伏時間可變的計算機系統之間通過分組交換進行時鐘同步的一個網路協議,位於OSI模型的應用層。自1985年以來,NTP是目前仍在使用的最古老的網際網路協議之一。NTP由特拉華大學的David L. Mill

Go語言中時間的實現

> 轉載請宣告出處哦~,本篇文章釋出於luozhiyun的部落格:https://www.luozhiyun.com/archives/444 最近在工作中有一個需求,簡單來說就是在短時間內會建立上百萬個定時任務,建立的時候會將對應的金額相加,防止超售,需要過半個小時再去核對資料,如果資料對不上就需要將加上

Bootstrap控制播圖的時間

pri spa ots int pretty trap interval 輪播 bootstra $(‘#identifier‘).carousel({ interval: 2000 })( 默認值5000,“#identifier”為最外層盒子的id )Boots

Lifeline功能介紹01——日歷及時間軸的查看

圖1 列表顯示 課堂 下拉 body 支持 image lin png   “Lifeline功能介紹”系列博客主要介紹目前為止已經實現的網站功能,包括:日歷及時間軸的查看、個人事件的添加、課堂信息的查詢等。 本篇介紹——日歷及時間軸的查看 #用戶登陸後會進入主頁界面,為日

Nginx訪問日誌、Nginx日誌切割、靜態文件不記錄日誌和過期時間介紹

LinuxNginx訪問日誌1. 進入配置文件[root@gary-tao src]# vim /usr/local/nginx/conf/nginx.conf //搜索log_format參考更改配置成如下:log_format aming '$remote_addr $http_x_forwar

mysql 時間字段介紹

ado date_add 5.7 assign format spec pro 技術 epo mysql時間類型大概有5種,如下圖 1、創建數據庫 create table t1 ( id int(11) NOT NULL AUTO_INCREMENT PRIMA