kafka中對時間輪的應用分析

2021-10-10 09:01:11 字數 3139 閱讀 4911

kafka中存在著大量的延時操作,比如延遲生產,延遲拉取,延遲刪除等,這些延時操作並不是基於jdk 自帶的timer或者delayqueue 實現,而是基於時間輪的概念自己實現了乙個延時定時器,jdk中timer和delayqueue的插入和刪除操作的平均時間複雜度為o(nlogn)並不能滿足kafka的高效能要求,而基於時間輪可以將插入和刪除操作的時間複雜度都降為 o(1)。

kafka中的時間輪是乙個儲存定時任務的環形佇列,底層採用陣列實現,陣列中的每個元素可以存放乙個定時任務列表(timertasklist),timertasklist是乙個環形的雙向鍊錶,鍊錶中的每個元素timertaskentry封裝了乙個真正的定時任務timertask

時間輪由固定格數(wheelsize)的時間格組成,每一格都代表當前時間輪的基本時間跨度(tickms),整個時間輪的總體時間跨度(interval)就是wheelsize*tickms

時間輪還有乙個表盤指標(currenttime),其值是tickms的整數倍,用來表示時間輪當前所處的時間,表示當前需要處理的時間格對應的timetasklist中的所有任務。

總之,整個時間輪的跨度是不會變的,隨著currenttime的不斷推進,當前時間輪所能處理的時間段也在不斷後移,總體時間範圍就是currenttimecurrenttime + interval之間。

那麼,問題又來了,如果乙個新的定時任務遠遠超過了當前的總體時間範圍,比如350ms,那怎麼辦呢?

為此,kafka引入了層級時間輪的概念,當任務到期時間遠遠超過當前時間輪所表示的時間範圍時,就會嘗試新增到上層時間輪中。

如上圖:

生活中我們常見的鐘錶就是一種具有三層結構的時間輪,第一層時間輪 tickms=1ms 、wheelsize=60 、interval=1min,此為秒鐘 ; 第二層 tickms= 1min、wheelsize=60 、interval= 1hour,此為分鐘 ;第三層 tickms=1hour 、 wheelsize= 12 、 interval= 12hours,此為時鐘 。

引入多層時間輪之後,對於之前所說的350ms的定時任務,就會公升級到第二層時間輪的時間格17所對應的timertasklist中去。

那麼隨著時間的推移,之前定時為350ms的任務執行時間也會越來越接近,比如,距離該任務執行時間還有15ms的時候,15ms已經處於第一層時間輪的interval範圍之內了,顯然該任務繼續放在第二層的時間格17的位置是不合理的,這裡就有乙個時間輪降級的操作,會將這個任務重新提交到層級時間輪中,此時該任務就會重新放在第一層時間輪中currenttime所指的時間格後的第15個時間格內等待被執行。

所以,所有位於第二及第二層時間輪以上的任務在執行前都會有乙個時間輪降級的過程,會從第n級,降到第n-1級,n-2級……直到降到第一級為止。

我們可以總結出,kafka的定時器只是持有第一層時間輪的引用,並不會直接持有其他高層時間輪的引用,但是每個時間輪都會有乙個指向更高一層時間輪的引用,隨著時間的推移,高層時間輪內的定時任務也會重新插入到時間輪內,直到插入到第一層時間輪內等待被最終的執行。

當然kafka的定時器任務執行並沒有這麼簡單,在kafka中,時間輪是專門用來執行插入和刪除timetaskentry的(即封裝真正的定時任務timetask的例項),而時間輪的推進照樣是借用了jdk的delayqueue來實現的。具體做法是將每個用到的timertasklist放入delayqueue,在delayqueue中會按照timertasklist的過期時間expiration來排序,expiration最小的排在頭部,kafka中有乙個專門的執行緒來從delayqueue中獲取到期的任務列表,然後就可以根據expiration來推進時間輪的時間,也可以處理獲取到的timetasklist,對裡面的timertaskentry執行過期操作或降級時間輪 。

這裡最難懂的就是delayqueue與 時間輪的關係,文章開頭說了delayqueue不能滿足kafka的高效能要求,那麼這裡怎麼還要用到delayqueue呢?

對於時間輪而言,timertaskentry的插入以及刪除,時間複雜度都為o(1),而delayqueue是乙個有序無界的blockingqueue,當乙個新的定時任務放入delayqueue的時候,首先要在佇列中找到該任務的位置,然後插進去,即使將若干個timetaskentry按照一定的規則封裝到timertasklist,然後將timertasklist插入到delayqueue,這時當乙個新的timertaskentry插入的時候,也是比較麻煩的乙個操作,另外如果只用delayqueue,時間推進的單位設定過大,則精度不足,過小則浪費資源,顯然是滿足不了kafka的高效能要求。

因此,kafka的設計者就使用了delayqueue+時間輪的方式,來保證kafka的高效能定時任務的執行,delayqueue負責時間輪的推進工作,時間輪則負責將每個定時任務timertaskentry按照時間順序插入以及刪除,然後又使用專門的乙個執行緒來從delayqueue中獲取到期的任務列表,然後執行對應的操作,這樣就保證了kafka的高效能執行。

C 中對時間的操作

1 datetime 數字型 system.datetime currenttime new system.datetime 1.1 取當前年月日時分秒 currenttime system.datetime.now 1.2 取當前年 int 年 currenttime.year 1.3 取當前月 ...

Object C中對時間的處理

一般有獲取具體的年月日和星期,兩個不同時間的差,某一天的前一天或後一天等 在這裡我只介紹獲取具體的年月日和星期,及某一天的前一天或後一天的方法 對時間的處理一般都會用到nsdate類和nscalendar類兩個類 獲取當前的時間是 nsdate nowdate nsdate date 獲取具體的年月...

對時間的封裝util

將乙個秒數的時間轉成形如00 00 00字串 public static string parsetime string date else else if integer.parseint curday integer.parseint day 2 else else else return da...