本文是【字節可視化系列】Kafka專欄文章。算法
經過本文你將瞭解到時間輪算法思想,層級時間輪,時間輪的升級和降級。
時間輪,是一種實現延遲功能(定時器)的巧妙算法,在Netty,Zookeeper,Kafka等各類框架中,甚至Linux內核中都有用到。數組
本文將參考Kafka的時間輪做爲例子講解。markdown
開始以前給你們看塊寶珀中華年曆表。框架
圖片來自寶珀官網
oop
至於價格.....這個話題略過。動畫
而時間輪,其設計正是來源於生活中的時鐘。spa
如圖就是一個簡單的時間輪:
線程
圖中大圓的圓心位置表示的是當前的時間,隨着時間推移, 圓心處的時間也會不斷跳動。設計
下面咱們對着這個圖,來講說Kafka的時間輪TimingWheel。3d
Kafka時間輪的底層就是一個環形數組,而數組中每一個元素都存放一個雙向鏈表TimerTaskList,鏈表中封裝了不少延時任務。
Kafka中一個時間輪TimingWheel是由20個時間格組成,wheelSize = 20;每格的時間跨度是1ms,tickMs = 1ms。參照Kafka,上圖中也用了20個灰邊小圓表示時間格,爲了動畫演示能夠看得清楚,咱們這裏每一個小圓的時間跨度是1s。
因此如今整個時間輪的時間跨度就是 tickMs * wheelSize ,也就是 20s。從0s到19s,咱們都分別有一個灰邊小圓來承載。
Kafka的時間輪還有一個錶盤指針 currentTime,表示時間輪當前所處的時間。也就是圖中用黑色粗線表示的圓,隨着時間推移, 這個指針也會不斷前進;
有了時間輪,如今能夠往裏面添加定時任務了。咱們用一個粉紅色的小圓來表示一個定時任務。
講清楚這些設定後,咱們就開始添加定時任務吧。
初始的時候, 時間輪的指針定格在0。此時添加一個超時時間爲2s的任務, 那麼這個任務將會插入到第二個時間格中。
當時間輪的指針到達第二個時間格時, 會處理該時間格上對應的任務。在動畫上就是讓紅色的小圓消失!
若是這個時候又插入一個延時時間爲8s的任務進來, 這個任務的過時時間就是在當前時間2s的基礎上加8s, 也就是10s, 那麼這個任務將會插入到過時時間爲10s的時間格中。
爲了解答上面的問題,咱們先來點魔法, 讓時間輪上的時間都動起來!
其實呢,當指針定格在2s的位置時, 時間格0s, 1s和2s就已是過時的時間格。
也就是說指針能夠用來劃分過時的時間格[0,2]和將來的時間格 [3,19]。而過時的時間格能夠繼續複用。好比過時的時間格0s就變成了20s, 存放過時時間爲20s的任務。
如圖是一個兩層的時間輪:
回到一開始的問題,在當前時間是2s的時候, 插入一個延時時間爲22s的任務,該任務過時時間爲24s。
當第一層時間輪容納不下時,進入第二層時間輪,並插入到過時時間爲[20,39]的時間格中。
咱們再來個例子,若是在當前時間是2s的時候, 插入一個延時時間爲350s的任務, 這個任務的過時時間就是在2s的基礎上加350s,也就是352s。
從圖中能夠看到,該任務插入到第二層時間輪過時時間爲[340,359]s的時間格中,也就是第17格的位置。
一般來講, 第二層時間輪的第0個時間格是用來表示第一層時間輪的, 這一格是存放不了任務的, 由於超時時間0-20s的任務, 第一層時間輪就能夠處理了。
可是! 事情每每沒這麼簡單, 咱們時間輪上的時間格都是能夠複用的! 那麼這在第二層時間輪上又是怎麼體現的呢?
下面是魔法時間, 咱們讓時間輪上的過時時間都動起來!
從圖中能夠看到,當第一層時間輪的指針定格在1s時,超時時間0s的時間格就過時了。而這個時候,第二層時間輪第0個時間格的時間範圍就從[0,19]分爲了過時的[0],和未過時的[1,19]。而過時的[0]就會被新的過時時間[400]複用。
第二層時間輪第0個時間格的過時時間範圍演變以下:
[0-19]
[400][1,19]
[400,401][2,19]
......
[400,419]
因此,若是在當前時間是2s的時候, 插入一個延時時間爲399s的任務, 這個任務的過時時間就是在2s的基礎上加399s,也就是401s。如圖,這個任務仍是會插到第二層時間輪第0個時間格中去。