成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

TimeWheel 算法介紹及在應(yīng)用上的探索

開發(fā)
本文從定時任務(wù)和時間輪算法的起源開始,對時間輪算法進行了介紹。詳細(xì)的闡述了時間輪的算法思想,以及簡單時間輪、帶 round 的時間輪以及分層時間輪這三種常見的時間輪模型,并給出了對應(yīng)的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)。

本文從追溯時間輪算法的出現(xiàn),介紹了時間輪算法未出現(xiàn)前,基于隊列的定時任務(wù)實現(xiàn),以及基于隊列的定時任務(wù)實現(xiàn)所存在的缺陷。接著我們介紹了時間輪算法的算法思想及其數(shù)據(jù)結(jié)構(gòu),詳細(xì)闡述了三種時間輪模型的數(shù)據(jù)結(jié)構(gòu)和優(yōu)劣性。

再次,我們介紹時間輪算法在 Dubbo 框架中的應(yīng)用,并給出了它在 Dubbo 中的主要實現(xiàn)方式。

最后,我們以項目中的某個服務(wù)架構(gòu)優(yōu)化出發(fā),介紹了目前設(shè)計中存在的缺陷,并借助來自中間件團隊的,包含時間輪算法實現(xiàn)的延遲 MQ,給出了優(yōu)化設(shè)計的方法。

第一章    定時任務(wù)及時間輪算法發(fā)展

1.1 時間輪算法的出現(xiàn)

在計算程序中,定時器用于指定一個具體的時間點去執(zhí)行某一個既定的任務(wù)。而時間輪算法就是這樣一種能夠?qū)崿F(xiàn)延遲功能(定時器)的巧妙算法。時間輪算法首次出現(xiàn)在1997年 George Varghese 和 Anthony Lauck 發(fā)表于IEEE期刊,名為“Hashed and Hierarchical Timing Wheels: Efficient Data Structures for Implementing a Timer Facility”的論文上。此文章指出,實現(xiàn)操作系統(tǒng)定時器模塊的常規(guī)算法需要 O(n)的時間復(fù)雜度啟動和維護計時器,對于更大問題規(guī)模(n),這樣的時間開銷是巨大的,文中提出并證明了,通過一種環(huán)狀桶的數(shù)據(jù)結(jié)構(gòu),可以做到使用 O(1)的時間復(fù)雜度,就可以啟動,停止和維護計時器,并介紹了對時間間隔劃分的處理,第一種方式是將所有的計時器時間間隔進行散列(Hash),這些時間間隔被散列到時間輪上特定的槽位中(Slot),第二種方式是利用多粒度定時輪組成具有層級結(jié)構(gòu)的組合,以擴展更大的時間范圍。這兩種結(jié)構(gòu)將在第二章中詳細(xì)介紹。

1.2 基于隊列的定時任務(wù)執(zhí)行模型

在計算機的世界中,只有待解決的問題大規(guī)模化以后,算法的價值才能夠得到最大化的體現(xiàn)。在介紹時間輪算法之前,我們有必要介紹另一種定時任務(wù)的實現(xiàn),即基于隊列的定時任務(wù)。隊列這種數(shù)據(jù)結(jié)構(gòu)無論是在操作系統(tǒng)中還是各編程語言如 Java 中都被大量使用,本文不再展開贅述。

下面從線程模型、定時任務(wù)種類和任務(wù)隊列的數(shù)據(jù)結(jié)構(gòu)三個方面展開詳細(xì)介紹:

(1)線程模型

用戶線程:負(fù)責(zé)定時任務(wù)的注冊;

輪詢線程:負(fù)責(zé)從任務(wù)隊列中掃描出符合執(zhí)行條件的任務(wù),例如任務(wù)的待執(zhí)行時間已經(jīng)到達(dá),輪詢線程將從隊列中取出該任務(wù),并交由異步線程池處理該任務(wù)。

異步線程池:專門負(fù)責(zé)任務(wù)的執(zhí)行。

(2)定時任務(wù)

定時任務(wù)主要分為一次性執(zhí)行的定時任務(wù)(Dubbo 中超時判斷)以及重復(fù)執(zhí)行的定時任務(wù),這兩種定時任務(wù)都很好理解,一次性執(zhí)行的定時任務(wù)在規(guī)定的未來某一時刻或距離現(xiàn)在的一段固定時長后執(zhí)行,分別對應(yīng)絕對值和相對值的概念。

而重復(fù)執(zhí)行的定時任務(wù)是在一次性執(zhí)行任務(wù)的基礎(chǔ)上多次重復(fù)執(zhí)行,這意味著,在上述線程協(xié)調(diào)工作中,當(dāng)重復(fù)執(zhí)行任務(wù)執(zhí)行完成一次后,將被重新放回任務(wù)隊列中。  

(3)任務(wù)隊列數(shù)據(jù)結(jié)構(gòu)

 從最簡單的數(shù)據(jù)結(jié)構(gòu)出發(fā),假設(shè)我們選用最基本的隊列,或者考慮到增減任務(wù)的方便,選擇雙向鏈表做為任務(wù)隊列,為任務(wù)隊列中的每個任務(wù)提供一個時間戳字段,這種實現(xiàn)的策略會產(chǎn)生哪些問題?

最大的問題是在查詢上,假設(shè)任務(wù)隊列中存在一些任務(wù),那么為了找出達(dá)到規(guī)定時刻的待執(zhí)行任務(wù),輪詢線程需要掃描全部任務(wù),此種數(shù)據(jù)結(jié)構(gòu)的時間復(fù)雜度為 O(n),而且存在大量的空輪詢,即大部分的任務(wù)都沒有達(dá)到執(zhí)行時間,這種效率幾乎是不可接受的。

為了提升查詢效率,可以嘗試從數(shù)據(jù)結(jié)構(gòu)出發(fā),利用有序隊列,在計算機的算法中,有序性可以顯著提高遍歷的效率,這樣一來,定時任務(wù)隊列輪詢線程從頭向尾遍歷時,在發(fā)現(xiàn)任意任務(wù)未達(dá)到規(guī)定執(zhí)行時間戳后,就可以停止遍歷。

但是維護有序性也需要付出代價,普通任務(wù)隊列入隊一個任務(wù)的時間復(fù)雜度僅僅是 O(1),而有序任務(wù)隊列入隊一個任務(wù)的時間復(fù)雜度為 O(nlogn)。其次,我們可以借鑒分治的思想,將任務(wù)隊列分成n份,利用多線程遍歷,在線程完全并發(fā)執(zhí)行的情況下,問題規(guī)模簡化到原來的1/n。但是多線程也會 CPU 執(zhí)行效率降低。

綜上分析,定時任務(wù)框架需要具有如下要素:

  1. 嚴(yán)格高效的數(shù)據(jù)結(jié)構(gòu),并不能基于簡單的隊列結(jié)構(gòu)來存儲任務(wù),否則輪詢的執(zhí)行效率永遠(yuǎn)無法提高。
  2. 簡單的并發(fā)模型:CPU 的線程非常寶貴,不應(yīng)占用過多線程資源。

時間輪算法解決了上述基于隊列的定時任務(wù)執(zhí)行模型的缺陷,因此時間輪算法思想在后面互聯(lián)網(wǎng)技術(shù)發(fā)展中得到了大量應(yīng)用,我們熟悉的 Linux Crontab,以及 Java 開發(fā)中常用的 Dubbo、Netty、Quartz、Akka、ZooKeeper、Kafka 等,幾乎所有的時間任務(wù)調(diào)度都采用了時間輪算法的思想。

值得一提的是,在 Dubbo 中,為了增強系統(tǒng)容錯,很多地方需要用到只需一次執(zhí)行的任務(wù)調(diào)度,比如消費者需要知道各個 RPC 調(diào)用是否超時,而在 Dubbo 最開始的實現(xiàn)中,是采用將所有的返回結(jié)果(defaultFuture),都放入一個集合中,并通過一個定時任務(wù),間隔掃描所有的 future,逐個判斷是否超時。這樣邏輯簡單,但是浪費性能,后面 Dubbo 借鑒了 Netty,引入了時間輪。

第二章    時間輪算法思想介紹及應(yīng)用場景介紹

2.1 時間輪簡介

時間輪實質(zhì)上是一種高效利用線程資源的任務(wù)調(diào)度模型,將大批量的任務(wù)全部整合進一個調(diào)度器中,從而對任務(wù)進行統(tǒng)一的調(diào)度管理,針對定時任務(wù),延時任務(wù)等事件的調(diào)度效率非常高。

時間輪算法的核心是:第一章中描述的對任務(wù)隊列進行輪詢的線程不再負(fù)責(zé)遍歷所有的任務(wù),而是僅僅遍歷時間刻度。時間輪算法好比指針不斷在時鐘上旋轉(zhuǎn)、遍歷,如果發(fā)現(xiàn)某一時刻上有任務(wù)(任務(wù)隊列),那么就會將任務(wù)隊列上的所有任務(wù)都執(zhí)行一遍,這樣便大幅度的減少了額外的掃描操作。

第一章中,我們提出了一個高效的定時任務(wù)框架需要具備嚴(yán)格高效的數(shù)據(jù)結(jié)構(gòu)和簡單的并發(fā)模型兩個特點,而時間輪模型正是具備了這樣的特點。

基于時間輪算法思想,后續(xù)也出現(xiàn)了很多種時間輪模型,目前流行的大致有三種,分別為簡單時間輪模型、帶有 round 的時間輪模型以及分層時間輪模型,下面將依次介紹這三種時間輪模型。

2.2 時間輪模型

2.2.1 簡單時間輪模型

簡單時間輪模型不再使用隊列作為數(shù)據(jù)結(jié)構(gòu),而是使用數(shù)組加鏈表的形式(很經(jīng)典的組合), 如下圖所示,該時間輪通過數(shù)組實現(xiàn),可以很方便地通過下標(biāo)定位到定時任務(wù)鏈路,因此,添加、刪除、執(zhí)行定時任務(wù)的時間復(fù)雜度為 O(1)。

圖片

 圖2.2.1 簡單時間輪模型

顯然,這種簡單時間輪就解決了任務(wù)隊列中遍歷效率低下的問題,輪詢線程遍歷到某一個時間刻度后,總是執(zhí)行對應(yīng)刻度上任務(wù)隊列中的所有任務(wù)(通常是將任務(wù)扔給異步線程池來處理),而不再需要遍歷檢查所有任務(wù)的時間戳是否達(dá)到要求。

通過增加槽(slot)的數(shù)量,可以細(xì)化的時間粒度以及得到更大的時間跨度,但是這樣的實現(xiàn)方式有巨大的缺陷:

  1. 當(dāng)時間粒度小,時間跨度大,而任務(wù)又很少的時候,時間槽的輪詢效率變低。
  2. 當(dāng)時間粒度小,時間槽數(shù)量多,而任務(wù)又很少時,很多槽位占用的內(nèi)存空間是沒有意義的。

2.2.2 帶有 round 的時間輪模型

類比循環(huán)數(shù)組的思想,后人設(shè)計了帶 round 的時間輪,這種時間輪的結(jié)構(gòu)如下圖所示:

圖片

 圖2.2.2 帶有round的時間輪模型

如圖2.2.2所示,expire 代表到期時間,round 表示時間輪要在轉(zhuǎn)動幾圈之后才執(zhí)行任務(wù),也就是說當(dāng)指針轉(zhuǎn)到某個 bucket 時,不能像簡單的單時間輪那樣直接執(zhí)行 bucket 下所有的任務(wù)。而且要去遍歷該 bucket 下的鏈表,判斷時間輪轉(zhuǎn)動的次數(shù)是否等于節(jié)點中的 round 值,只有當(dāng) expire 和 round 都相同的情況下,才能執(zhí)行任務(wù)。

這種結(jié)構(gòu)的時間輪明顯減少了所需刻度的個數(shù),即彌補了簡單時間輪在時間槽位較多,而任務(wù)較少情況下內(nèi)存空間浪費的問題。

但是這種結(jié)構(gòu)的時間輪并不能減少輪詢線程的輪詢次數(shù),效率相對較低。

2.2.3 分層時間輪模型

分層時間輪也是一種對簡單時間輪的改良方案,它的設(shè)計理念可以類比于日常生活中的時鐘,分別有時、分、秒三個層級,并且每個輪盤分別具有24、60、60個刻度,因此,只需要144個刻度,即可表示一天的時間,而這種表示方式的優(yōu)勢在于,倍數(shù)級別時間表示的新增,只需要常數(shù)級別的刻度增加。例如,在144個刻度可表示的一天時間的基礎(chǔ)上,新增30個刻度,即可精細(xì)表示一個月的時間。

圖片

 圖2.2.3 分層時間輪模型

分層時間輪的工作方式為低層級的時間輪帶動高層級的時間輪轉(zhuǎn)動,圖中箭頭為任務(wù)的“下放”,例如,2號8點40分0秒執(zhí)行的任務(wù),當(dāng)天輪轉(zhuǎn)動到刻度2時,會將第2天的任務(wù),下放到對應(yīng)時輪刻度為8的槽位中,當(dāng)時輪轉(zhuǎn)動到8時,會將任務(wù)繼續(xù)下放到分輪刻度為40的槽位中,直至到最低層次的時間輪,轉(zhuǎn)動到該槽位時,將該槽位中的任務(wù),全部執(zhí)行。

針對時間復(fù)雜度,這種時間輪對比帶有 round 的時間輪不再遍歷計算對比任務(wù)的 round,而是直接全部取出執(zhí)行。

針對空間復(fù)雜度,分層時間輪利用維度上升的思路對時間輪進行分層,每個層級的時間粒度對應(yīng)一個時間輪,多個時間輪之間進行級聯(lián)協(xié)作。

2.3 時間輪應(yīng)用場景介紹

時間輪作為高效的調(diào)度模型,在各種場景均有廣泛的應(yīng)用,常見的場景主要有如下幾個:

(1)定時器

時間輪常用于實現(xiàn)定時器,可以在指定時間執(zhí)行特定任務(wù)。定時器可以用于周期性任務(wù)、超時任務(wù)等,如輪詢 I/O 事件、定期刷新緩存、定時清理垃圾數(shù)據(jù)等。

(2)負(fù)載均衡

時間輪可以用于實現(xiàn)負(fù)載均衡算法,將請求分配到不同的服務(wù)器上,避免單個服務(wù)器負(fù)載過重。時間輪可以根據(jù)服務(wù)器的負(fù)載情況來動態(tài)調(diào)整分配策略,實現(xiàn)動態(tài)負(fù)載均衡。

(3)事件驅(qū)動

時間輪可以用于實現(xiàn)事件驅(qū)動模型,將事件分配到不同的處理器上,提高并發(fā)處理能力。事件可以是 I/O 事件、定時事件、用戶事件等,時間輪可以根據(jù)事件的類型和優(yōu)先級來動態(tài)調(diào)整分配策略,實現(xiàn)高效的事件驅(qū)動模型。

(4)數(shù)據(jù)庫管理

時間輪可以用于實現(xiàn)數(shù)據(jù)庫管理,將數(shù)據(jù)分配到不同的存儲設(shè)備上,提高數(shù)據(jù)讀寫效率。時間輪可以根據(jù)數(shù)據(jù)的類型、大小和訪問頻率等來動態(tài)調(diào)整數(shù)據(jù)分配策略,實現(xiàn)高效的數(shù)據(jù)庫管理。

(5)其他應(yīng)用

時間輪還可以用于其他一些應(yīng)用,如消息隊列、任務(wù)調(diào)度、網(wǎng)絡(luò)流量控制等,具體應(yīng)用取決于具體的需求和場景。

第三章     時間輪在 Dubbo 的應(yīng)用與實現(xiàn)

3.1 Dubbo 中時間輪的應(yīng)用

Dubbo 的設(shè)計中,客戶端在調(diào)用服務(wù)端的時候,會對任務(wù)進行計時,如果任務(wù)超時,那么會被檢測到,并重試請求。在 Dubbo 最開始的實現(xiàn)中,是采用將所有的返回結(jié)果(defaultFuture),都放入一個集合中,并通過一個定時任務(wù),間隔掃描所有的 future,逐個判斷是否超時。

這樣邏輯簡單,但是浪費性能,后面 Dubbo 借鑒了 Netty,引入了時間輪。任務(wù)交由時間輪管理,由專門的線程進行調(diào)度。

3.2 Dubbo 中時間輪的實現(xiàn)

Dubbo 中時間輪算法的實現(xiàn),主要有一個類和三個接口:

圖片

首先是 Timer 接口,這個一個調(diào)度的核心接口,主要用于后臺的一次性調(diào)度,我們僅介紹 newTimeOut 方法,這個方法就是把一個任務(wù)扔給調(diào)度器執(zhí)行,第一個參數(shù)類型 TimerTask,即需要執(zhí)行的任務(wù)。

圖片

接下來是 TimeTask 接口,它只有一個方法 run,參數(shù)類型是 Timeout,我們注意到上面 Timer 接口的 newTimeout  這個方法返回的參數(shù)就是 Timeout,和此處的入?yún)⑾嗤瑢嶋H這里傳入的 Timeout 參數(shù)就是 newTimeout 的返回值。

圖片

Timeout 對象與 TimerTask 對象一一對應(yīng),兩者的關(guān)系類似于線程池返的 Future 對象與提交到線程池中的任務(wù)對象之間的關(guān)系。

最后是 TimeOut 接口,它代表的是對一次任務(wù)的處理,其中有幾個方法,從介紹上即可看出各方法用途,這里不再贅述。

圖片

上述幾個接口從邏輯上構(gòu)成了一個任務(wù)調(diào)度系統(tǒng)。下面是任務(wù)調(diào)度系統(tǒng)的核心,即時間輪調(diào)度器的實現(xiàn)-- HashedWheelTimer。

仔細(xì)看它的類上注釋可以發(fā)現(xiàn),該方法并不能提供精確的計時,而是檢測每個 tick 中(也就是時間輪中的一個時間槽),是否有 TimerTask,其期望執(zhí)行時間已經(jīng)落后于當(dāng)前時間,如果是則執(zhí)行該任務(wù)。任務(wù)執(zhí)行時間的精確度可以通過細(xì)化時間槽來提升。

默認(rèn)的 tick duration 是100毫秒,大部分網(wǎng)絡(luò)應(yīng)用中,I/O 超時并非必須是精準(zhǔn)的,例如5秒超時,實際上稍晚一會也是可以的,因此這個默認(rèn)值無需修改。

圖片

這個類維護了一種稱為“wheel”的數(shù)據(jù)結(jié)構(gòu),也就是我們說的時間輪。簡單地說,一個 wheel 就是一個 hash table,它的 hash 函數(shù)是任務(wù)的截止時間,也就是我們要通過 hash 函數(shù)把這個任務(wù)放到它應(yīng)該在的時間槽中,這樣隨著時間的推移,當(dāng)我們進入某個時間槽中時,這個槽中的任務(wù)也剛好到了它該執(zhí)行的時間。

這樣就避免了在每一個槽中都需要檢測所有任務(wù)是否需要執(zhí)行。在 HashedWheelTimer 的構(gòu)造函數(shù)中,最重要的是 createWheel 方法,忽略基本的參數(shù)校驗,只看方法主流程,首先是對時間槽數(shù)量的規(guī)范化處理,處理方式為將構(gòu)造時傳入的數(shù)量,修改為大于等于它的最小的2的次冪。為什么這樣處理以及處理的具體方式,有興趣可以研究下源代碼。

圖片

接著則是創(chuàng)建時間槽數(shù)組,最后是初始化時間槽數(shù)組的每個參數(shù)。

下面介紹下 newTimeout 方法,這個方法的主要作用是向調(diào)度器中添加一個待執(zhí)行的任務(wù),同樣忽略基本的參數(shù)校驗,主體流程為:

  1. 第一步將等待調(diào)度的任務(wù)數(shù)+1,如果超過了最大限制,則-1并拋出異常。
  2. 第二步則調(diào)用 start 方法,啟動時間輪。
  3. 第三步計算當(dāng)前任務(wù)的截止時間,并做防溢出處理。
  4. 構(gòu)造一個 TimeOut ,并放入等待隊列。

圖片

這里我們展開比較重要的 start 方法,首先獲取 worker 的運行狀態(tài),如果是初始化狀態(tài),則更新成已啟動狀態(tài),啟動 workThread 線程,若是其他狀態(tài),則做其他相應(yīng)的處理。接著是等待 workThread 將 startTime 初始化完成(在 Worker 的 run 方法中初始化完成),之所以需要等待 startTime 初始化完成,是因為 newTimeout 方法中,start 方法調(diào)用后也用到了這個 startTime,不這樣做,任務(wù)的截止時間計算會有問題。

圖片

至此,我們介紹了利用 HashedWheelTimer 添加一個任務(wù)的主體流程,接下來是時間輪的內(nèi)部運轉(zhuǎn)。

首先是 HashedWheelTimer 的內(nèi)部類 Worker,其中 run 方法的主體流程如下:

1.初始化 startTime,這里與上文中 start 方法內(nèi)部對應(yīng)。初始化后,利用閉鎖 CountDownLatch 通知等待線程往下執(zhí)行。

圖片

2.當(dāng)定時器處于已啟動狀態(tài)時,不停地推進 ticket,推進的過程分解為:

  • 等待下一個 ticket 的到來。
  • ticket 到來后,計算該 ticket 對應(yīng)時間輪的槽位(取模運算)。
  • 處理已取消的任務(wù)隊列。
  • 獲取當(dāng)前時間槽,并將待處理任務(wù)隊列中的任務(wù)放到槽中。
  • 執(zhí)行當(dāng)前時間槽中的任務(wù)。

圖片

3. 如果時間輪已經(jīng)停止了,則執(zhí)行以下流程:

  • 清理所有時間槽中的未處理任務(wù)調(diào)度。
  • 清理待處理任務(wù)調(diào)度隊列,將未取消的加入到未處理集合中。
  • 處理已取消的任務(wù)隊列。

圖片

我們重點關(guān)注下定時器啟動狀態(tài)下的第3步,獲取當(dāng)前時間槽,并將待處理任務(wù)隊列中的任務(wù)放到槽中的方法 transferTimeoutsToBuckets,其流程為以下幾個步驟(這里規(guī)定循環(huán)了有限次,防止待處理隊列過大,導(dǎo)致本次添加到槽耗費時間過長):

  • 從待處理任務(wù)調(diào)度隊列中取出第一個任務(wù),進行校驗。
  • 根據(jù)取出的待處理任務(wù)調(diào)度,計算出一個槽。
  • 設(shè)置此任務(wù)調(diào)度的剩余圈數(shù)(從這里看出 Dubbo 用的是我們在2.2.2中介紹的“帶有 round 的時間輪”)。
  • 取計算出的槽和當(dāng)前槽中的較大者,并進行取模。
  • 將此任務(wù)調(diào)度加入對應(yīng)的槽中。

圖片

總結(jié):這部分內(nèi)容我們分別從向調(diào)度器中添加任務(wù)的主體流程和時間輪內(nèi)部運轉(zhuǎn)兩個部分,簡單介紹了 Dubbo 中時間輪的實現(xiàn)。

如果感興趣,可以學(xué)習(xí)其源代碼,里面很多代碼設(shè)計非常巧妙,比如 startTime 初始化及初始化完成后的線程間通信實現(xiàn),這些設(shè)計思路對筆者這樣的初學(xué)者來說很有益處。

第四章    時間輪算法的應(yīng)用展望

筆者在剛開始工作時,設(shè)計過一個叫做下載中心的服務(wù),這個服務(wù)的功能為導(dǎo)出和下載項目中的數(shù)據(jù)文件,實際的定位是為了減少異步線程過多而影響各個核心業(yè)務(wù),因此將其功能抽取出來,從而達(dá)到減少核心業(yè)務(wù)壓力的目標(biāo)。

下載中心的初步設(shè)計,考慮到并發(fā)請求以及文件過大帶來的內(nèi)存溢出問題,除了采取各種方式避免外,整體思路是,預(yù)計特別大的文件,先將任務(wù)記錄進行持久化,并通過后臺線程池慢慢執(zhí)行這些任務(wù),通過任務(wù)記錄,主動拉取數(shù)據(jù)、生成文件等。

模型類似于 Netty 中的 BOSS-WORKER 的模式,BOSS 線程負(fù)責(zé)定時從數(shù)據(jù)庫中查出未消費的任務(wù),并將其分配給 worker 線程池進行消費,如圖4.1所示。

圖片

 圖4.1 改造前應(yīng)用服務(wù)任務(wù)消費模式示意圖

當(dāng)前設(shè)計雖然可以做到防止內(nèi)存溢出等問題,但是這樣的設(shè)計也存在一定的缺陷:

  1. 如果后續(xù)用戶量增多,可以考慮水平擴充服務(wù)的數(shù)量,但是用于持久化任務(wù)記錄的數(shù)據(jù)庫會成為瓶頸。
  2. 即使 BOSS 線程不難做到避免任務(wù)的重復(fù)消費,但是待執(zhí)行任務(wù)的查詢效率會大大降低。
  3. 整個服務(wù)太過于依賴 BOSS 線程。

因此,考慮一種方式替代這種 BOSS-WORKER 模式,目前想到的一種方式為 MQ 消息隊列,將待執(zhí)行的任務(wù)信息投至 MQ 隊列當(dāng)中,然后該服務(wù)對其進行消費。這樣的做法,即可解決上述3個問題,并具備維持任務(wù)有序性的優(yōu)勢。

但是內(nèi)存易溢出的問題仍然存在,因此,考慮限制消費任務(wù)的線程并發(fā)數(shù)量。如果超過這個數(shù)量,則不再消費任務(wù),而是重新投遞任務(wù)至 MQ 隊列中。這里,我們有更好的做法,即需要將任務(wù)重新投遞至 MQ 隊列時,做一些延時的處理,防止反復(fù)重新投遞任務(wù)。整體流程如圖4.2所示:

圖片

圖4.2 改造后應(yīng)用服務(wù)任務(wù)消費模式示意圖

圖中,綠色模塊為整個系統(tǒng)設(shè)計中的用以調(diào)度定時任務(wù)的任務(wù)調(diào)度模塊,利用時間輪來統(tǒng)一管理這些定時任務(wù)。

對于短暫延時和長延遲的消息,我們都期望延時盡可能的精確,而對于長延時的消息,我們還要對其進行持久化,也就是暫存。等到消息快要到期時,再重新取出,進行投遞。

而這種長延時消息的持久化,與我們圖4.1所示定時從數(shù)據(jù)庫取任務(wù)所遇到的瓶頸是一致的。

我們更期望有成熟的框架,能夠提供 1.長延遲任務(wù)的持久化 以及 2. 任務(wù)調(diào)度 的能力。從中間件平臺組提供的 MQ 中,我們發(fā)現(xiàn)目前它是已經(jīng)支持 包含這兩個能力的延遲 MQ 功能的。延遲 MQ 架構(gòu)大致如下圖所示:

圖片

           圖4.3 延遲MQ消息處理流程圖

具體延遲消息的發(fā)送和處理的流程如下圖所示:

圖片

圖4.4 延遲消息發(fā)送和處理流程示意圖

實際上,該延遲 MQ 的實現(xiàn),正是由時間輪實現(xiàn)的調(diào)度 以及利用 MongoDB 數(shù)據(jù)庫 實現(xiàn)的持久化,這與我們所期望的能力完全一致,完全可以滿足我們的需求。

總結(jié)

本文從定時任務(wù)和時間輪算法的起源開始,對時間輪算法進行了介紹。詳細(xì)的闡述了時間輪的算法思想,以及簡單時間輪、帶 round 的時間輪以及分層時間輪這三種常見的時間輪模型,并給出了對應(yīng)的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)。

接著以 Dubbo 為例,介紹了時間輪模型在 Dubbo 中的應(yīng)用,從源碼出發(fā),介紹了該算法在 Dubbo 中的主要實現(xiàn)。

最后,我們介紹了筆者自身所做過的一個小模塊,展開分析了該模塊功能目前所遇到的瓶頸,并給出了通過融合了時間輪算法的延遲 MQ 來優(yōu)化當(dāng)前設(shè)計的思路。

參考文獻:

Hashed and Hierarchical Timing Wheels: EfficientData Structures for Implementing a Timer Facility.

責(zé)任編輯:龐桂玉 來源: vivo互聯(lián)網(wǎng)技術(shù)
相關(guān)推薦

2023-07-14 07:15:13

2022-08-12 15:02:31

應(yīng)用探索

2024-10-23 12:46:32

數(shù)據(jù)飛輪數(shù)據(jù)應(yīng)用

2017-03-20 16:35:36

2021-12-01 00:05:03

Js應(yīng)用Ebpf

2019-11-12 15:45:07

區(qū)塊鏈數(shù)字貨幣智慧城市

2017-04-12 15:30:43

OpenCVKMeans算法

2015-06-12 11:23:27

光纖布線技術(shù)

2020-08-05 10:28:17

區(qū)塊鏈制造業(yè)區(qū)塊鏈應(yīng)用

2009-05-13 10:06:42

2009-12-21 17:40:25

WCF會話

2012-10-11 17:03:32

IBMdw

2022-04-19 09:53:06

云數(shù)據(jù)庫云計算數(shù)據(jù)庫

2024-07-22 09:10:04

大語言模型推薦系統(tǒng)人工智能

2011-09-02 19:01:53

AupeoiPad應(yīng)用音樂

2021-09-16 09:27:55

云計算5G云應(yīng)用

2010-03-19 14:59:00

python Stri

2019-06-06 08:52:00

2010-06-24 17:11:01

Linux chkco

2022-08-25 11:11:17

模型應(yīng)用
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 久久久国产精品入口麻豆 | 亚洲一级毛片 | 黄网站涩免费蜜桃网站 | av国产在线观看 | 波多野结衣中文字幕一区二区三区 | 久久国产成人午夜av影院武则天 | 黄色在线观看国产 | 日韩av在线免费 | 国产高清在线 | 欧美一级片 | 亚洲视频自拍 | 午夜爱爱网 | 自拍偷拍视频网 | 天天干天天草 | 一区二区三区四区免费在线观看 | 欧美精品99| 欧美黄色片在线观看 | 国产精品美女在线观看 | 国产一在线观看 | 国产一区在线视频 | 亚洲精品99999| 日韩精品久久久 | av 一区二区三区 | 亚洲一区二区三区四区av | 中文字幕二区 | www日本高清视频 | 久久久91精品国产一区二区三区 | 在线亚洲免费视频 | 国产成人一区二区三区精 | 日韩一区二区三区在线视频 | 久久99国产精品 | 国产高清久久久 | 成人av一区 | 精品久久久久久 | 一级毛片成人免费看a | 中文字幕在线一区二区三区 | 精产嫩模国品一二三区 | 另类a v| 一区二区福利视频 | 天天久久 | 亚洲欧美一区二区三区在线 |