1. Disruptor簡(jiǎn)單介紹

Disruptor是一個(gè)由LMAX開源的Java并發(fā)框架。LMAX是一種新型零售金融交易平臺(tái),這個(gè)系統(tǒng)是建立在 JVM 平臺(tái)上,核心是一個(gè)業(yè)務(wù)邏輯處理器,它能夠在一個(gè)線程里每秒處理 6 百萬訂單。業(yè)務(wù)邏輯處理器完全是運(yùn)行在內(nèi)存中(in-memory),使用事件源驅(qū)動(dòng)方式(event sourcing),具有低延遲,高吞吐的特性。

disruptor有多快?官方給出了和ArrayBlockingQueue的比較圖表:

移動(dòng)開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),手機(jī)維修培訓(xùn),手機(jī)軟件培訓(xùn)

Disruptor可以用來解決并發(fā)編程中的一個(gè)普遍的問題: 消息隊(duì)列的處理(producer和consumer)。

2. 為什么Disruptor如此之快

Disruptor 相對(duì)于傳統(tǒng)方式的優(yōu)點(diǎn):

  • 無鎖,沒有競(jìng)爭(zhēng)

  • 所有訪問者都記錄自己的序號(hào)的實(shí)現(xiàn)方式,允許多個(gè)生產(chǎn)者與多個(gè)消費(fèi)者共享相同的數(shù)據(jù)結(jié)構(gòu)

  • 緩存行填充,解決偽共享,提高cache命中率

  • 環(huán)形數(shù)組RingBuffer,避免GC開銷

3. Disruptor結(jié)構(gòu)分析

在了解disruptor如何工作之前,我們先看一下disruptor一些重要組件的介紹(翻譯自官方文檔,略有修改):

  • Ring Buffer:Ring Buffer通常被認(rèn)為是Disruptor的主要方面,但是從3.0開始Ring Buffer只負(fù)責(zé)數(shù)據(jù)(Events)的存儲(chǔ)和更新。對(duì)于一些高級(jí)用例,完全可以由用戶自己替換。

  • Sequence:Disruptor利用Sequences來標(biāo)志一個(gè)特定的組件,每一個(gè)消費(fèi)者(EventProcessor)都維護(hù)一個(gè)Sequence。Disruptor中大多數(shù)的并發(fā)代碼都是依賴于這些Sequence的移動(dòng),生產(chǎn)者對(duì)RingBuffer的互斥訪問,生產(chǎn)者與消費(fèi)者之間的協(xié)調(diào)以及消費(fèi)者之間的協(xié)調(diào),都是通過Sequence實(shí)現(xiàn)。幾乎每一個(gè)重要的組件都包含Sequence。由于需要在線程間共享,所以Sequence是引用傳遞,并且是線程安全的;再次,Sequence支持CAS操作;最后,為了提高效率,Sequence通過padding來避免偽共享。

  • Sequencer:Sequencer是Disruptor的真正的核心,此接口有兩個(gè)實(shí)現(xiàn)類 SingleProducerSequencer、MultiProducerSequencer ,它們定義在生產(chǎn)者和消費(fèi)者之間快速、正確地傳遞數(shù)據(jù)的并發(fā)算法。

  • Sequence Barriers:Sequence Barriers是由Sequencer創(chuàng)建的,包含Sequencer主發(fā)布的Sequence的引用和任何一個(gè)依賴消費(fèi)者的Sequences。它包含了判斷是否有任何事件可供消費(fèi)者處理的邏輯。

  • Wait Strategy:等待策略決定了消費(fèi)者會(huì)等待event被生產(chǎn)者放入Disruptor。Disruptor提供了多個(gè)等待策略的實(shí)現(xiàn)。1. BusySpinWaitStrategy:自旋等待,類似Linux Kernel使用的自旋鎖。低延遲但同時(shí)對(duì)CPU資源的占用也多。2. BlockingWaitStrategy :使用鎖和條件變量。CPU資源的占用少,延遲大。3. SleepingWaitStrategy :在多次循環(huán)嘗試不成功后,選擇讓出CPU,等待下次調(diào)度,多次調(diào)度后仍不成功,嘗試前睡眠一個(gè)納秒級(jí)別的時(shí)間再嘗試。這種策略平衡了延遲和CPU資源占用,但延遲不均勻。5. YieldingWaitStrategy :在多次循環(huán)嘗試不成功后,選擇讓出CPU,等待下次調(diào)度。平衡了延遲和CPU資源占用,但延遲比較均勻。6. PhasedBackoffWaitStrategy :上面多種策略的綜合,CPU資源的占用少,延遲大。

  • Event:數(shù)據(jù)從生產(chǎn)者傳遞給消費(fèi)者的數(shù)據(jù)單元。

  • EventProcessor:處理Disruptor中的events的主事件循環(huán),擁有消費(fèi)者Sequence的所有權(quán)。其中BatchEventProcessor即實(shí)現(xiàn)了有效率的event loop,而且可以回調(diào)給實(shí)現(xiàn)了EventHandler接口的類。

  • EventHandler:Disruptor 定義的事件處理接口,由用戶實(shí)現(xiàn),用于處理事件,是Consumer的真正實(shí)現(xiàn)。

  • Producer:即生產(chǎn)者,只是泛指調(diào)用 Disruptor 發(fā)布事件的用戶代碼,Disruptor 沒有定義特定接口或類型。

將這些元素放入Disruptor的context?中,Disruptor的整體結(jié)構(gòu)圖如下:

移動(dòng)開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),手機(jī)維修培訓(xùn),手機(jī)軟件培訓(xùn)

多播事件

Queue和Disruptor之間最大的差異。當(dāng)有多個(gè)消費(fèi)者監(jiān)聽在同一Disruptor的所有事件,一個(gè)單一的事件只會(huì)被發(fā)送到一個(gè)單一的消費(fèi)者。Disruptor一個(gè)使用的case是當(dāng)你需要對(duì)同樣的數(shù)據(jù)進(jìn)行不一樣的操作的時(shí)候。LMAX典型的例子是,我們有三個(gè)操作,日志(輸入數(shù)據(jù)寫入持久性日志文件),復(fù)制(將輸入數(shù)據(jù)發(fā)送到另一臺(tái)機(jī)器以確保有數(shù)據(jù)的遠(yuǎn)程復(fù)制),和業(yè)務(wù)邏輯(實(shí)際處理工作)。普通的Executor-style處理,可能是利用WorkPool并行的來處理這些不同的事件。這樣卻不是實(shí)現(xiàn)這個(gè)目標(biāo)最有效的途徑。

如上圖所示,我們有三個(gè)EventHandler(JournalConsumer, ReplicationConsumer and ApplicationConsumer)監(jiān)聽著Disruptor,每一個(gè)Handler都會(huì)順序的收到Disruptor里所有可用的消息,這樣就使得這些消費(fèi)者可以并行的處理這些消息了。

為了支持現(xiàn)實(shí)中并行處理的應(yīng)用,必須支持消費(fèi)者之間的協(xié)調(diào)?;氐缴厦娴睦樱乐箻I(yè)務(wù)邏輯的消費(fèi)還在繼續(xù),日志和復(fù)制的消費(fèi)者已經(jīng)完成了他們的任務(wù)是必須的。我們把這個(gè)概念稱為門,或者更準(zhǔn)確地說,這個(gè)行為的超級(jí)集合的特征叫做門。門發(fā)生在兩個(gè)地方。首先,我們需要確保生產(chǎn)者不超過消費(fèi)者。這是通過添加有關(guān)消費(fèi)者到Disruptor時(shí)通過調(diào)用RingBuffer.addgatingconsumers() 實(shí)現(xiàn)的。其次,通過實(shí)現(xiàn)一個(gè)SequenceBarrier(內(nèi)存屏障)的結(jié)構(gòu)可以實(shí)現(xiàn)必須先完成某些操作的需求。

參考圖1,有三個(gè)消費(fèi)者監(jiān)聽喚醒隊(duì)列中的事件,在圖中有一個(gè)依賴圖,ApplicationConsumer依賴于 JournalConsumer 和 ReplicationConsumer,這就說明 JournalConsumer 和 ReplicationConsumer可以互相自由的并發(fā),這層依賴關(guān)系可以從 ApplicationConsumer的 SequenceBarrier連接到 JournalConsumer和 ReplicationConsumer的 Sequences看出來。值得注意的是 Sequencer和下游消費(fèi)者之間的關(guān)系。作用之一就是確保發(fā)布不會(huì)覆蓋Ring Buffer。為了做到這一點(diǎn),下游消費(fèi)者沒有一個(gè)序列比RingBuffer的Sequence還要小,比RingBuffer的size還要小,然而,利用這個(gè)依賴圖可以做一些有意思的操作,因?yàn)锳pplicationConsumers Sequence是小于JournalConsumer 和 ReplicationConsumer(這就是依賴圖所保證的),Sequencer只用關(guān)注ApplicationConsumer的Sequence即可,其實(shí)一般意義上,Sequencer只用知道消費(fèi)者的Sequences??依賴樹中的葉子節(jié)點(diǎn)即可。

事件預(yù)分配

Disruptor的設(shè)計(jì)的一個(gè)目標(biāo)就是能被用在一個(gè)低延遲的環(huán)境中。在低延遲系統(tǒng)中,必須減少或移除內(nèi)存分配操作,基于Java開發(fā)的目的就是減少垃圾回收。(在低延遲的C/C++系統(tǒng)中,大內(nèi)存分配也存在問題,因?yàn)閮?nèi)存分配器也會(huì)存在競(jìng)爭(zhēng))

為了實(shí)現(xiàn)低延遲,Disruptor允許用戶對(duì)事件的內(nèi)存進(jìn)行預(yù)分配,在構(gòu)造過程和用戶提供的EventFactory中都會(huì)在Disruptor 的 RingBuffer中為每個(gè)實(shí)體分配。當(dāng)發(fā)布新數(shù)據(jù)到Disruptor中,API就會(huì)允許用戶獲取構(gòu)造方法的對(duì)象,以至于可以調(diào)用方法或者更新字段。Disruptor對(duì)這些操作提供并發(fā)安全性的保障。

可選的無鎖操作

另一個(gè)關(guān)鍵的實(shí)現(xiàn)低延遲的細(xì)節(jié)就是在Disruptor中利用無鎖的算法,所有內(nèi)存的可見性和正確性都是利用內(nèi)存屏障或者CAS操作。使用CAS來保證多線程安全,與大部分并發(fā)隊(duì)列使用的鎖相比,CAS顯然要快很多。CAS是CPU級(jí)別的指令,更加輕量,不必像鎖一樣需要操作系統(tǒng)提供支持,所以每次調(diào)用不需要在用戶態(tài)與內(nèi)核態(tài)之間切換,也不需要上下文切換。

只有一個(gè)用例中鎖是必須的,那就是BlockingWaitStrategy(阻塞等待策略),唯一的實(shí)現(xiàn)方法就是使用Condition實(shí)現(xiàn)消費(fèi)者在新事件到來前等待。許多低延遲系統(tǒng)使用忙等待去避免Condition的抖動(dòng),然而在系統(tǒng)忙等待的操作中,性能可能會(huì)顯著降低,尤其是在CPU資源嚴(yán)重受限的情況下,例如虛擬環(huán)境下的WEB服務(wù)器。

參考資料:

1. Disruptor簡(jiǎn)單介紹

Disruptor是一個(gè)由LMAX開源的Java并發(fā)框架。LMAX是一種新型零售金融交易平臺(tái),這個(gè)系統(tǒng)是建立在 JVM 平臺(tái)上,核心是一個(gè)業(yè)務(wù)邏輯處理器,它能夠在一個(gè)線程里每秒處理 6 百萬訂單。業(yè)務(wù)邏輯處理器完全是運(yùn)行在內(nèi)存中(in-memory),使用事件源驅(qū)動(dòng)方式(event sourcing),具有低延遲,高吞吐的特性。

disruptor有多快?官方給出了和ArrayBlockingQueue的比較圖表:

移動(dòng)開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),手機(jī)維修培訓(xùn),手機(jī)軟件培訓(xùn)

Disruptor可以用來解決并發(fā)編程中的一個(gè)普遍的問題: 消息隊(duì)列的處理(producer和consumer)。

2. 為什么Disruptor如此之快

Disruptor 相對(duì)于傳統(tǒng)方式的優(yōu)點(diǎn):

  • 無鎖,沒有競(jìng)爭(zhēng)

  • 所有訪問者都記錄自己的序號(hào)的實(shí)現(xiàn)方式,允許多個(gè)生產(chǎn)者與多個(gè)消費(fèi)者共享相同的數(shù)據(jù)結(jié)構(gòu)

  • 緩存行填充,解決偽共享,提高cache命中率

  • 環(huán)形數(shù)組RingBuffer,避免GC開銷

3. Disruptor結(jié)構(gòu)分析

在了解disruptor如何工作之前,我們先看一下disruptor一些重要組件的介紹(翻譯自官方文檔,略有修改):

  • Ring Buffer:Ring Buffer通常被認(rèn)為是Disruptor的主要方面,但是從3.0開始Ring Buffer只負(fù)責(zé)數(shù)據(jù)(Events)的存儲(chǔ)和更新。對(duì)于一些高級(jí)用例,完全可以由用戶自己替換。

  • Sequence:Disruptor利用Sequences來標(biāo)志一個(gè)特定的組件,每一個(gè)消費(fèi)者(EventProcessor)都維護(hù)一個(gè)Sequence。Disruptor中大多數(shù)的并發(fā)代碼都是依賴于這些Sequence的移動(dòng),生產(chǎn)者對(duì)RingBuffer的互斥訪問,生產(chǎn)者與消費(fèi)者之間的協(xié)調(diào)以及消費(fèi)者之間的協(xié)調(diào),都是通過Sequence實(shí)現(xiàn)。幾乎每一個(gè)重要的組件都包含Sequence。由于需要在線程間共享,所以Sequence是引用傳遞,并且是線程安全的;再次,Sequence支持CAS操作;最后,為了提高效率,Sequence通過padding來避免偽共享。

  • Sequencer:Sequencer是Disruptor的真正的核心,此接口有兩個(gè)實(shí)現(xiàn)類 SingleProducerSequencer、MultiProducerSequencer ,它們定義在生產(chǎn)者和消費(fèi)者之間快速、正確地傳遞數(shù)據(jù)的并發(fā)算法。

  • Sequence Barriers:Sequence Barriers是由Sequencer創(chuàng)建的,包含Sequencer主發(fā)布的Sequence的引用和任何一個(gè)依賴消費(fèi)者的Sequences。它包含了判斷是否有任何事件可供消費(fèi)者處理的邏輯。

  • Wait Strategy:等待策略決定了消費(fèi)者會(huì)等待event被生產(chǎn)者放入Disruptor。Disruptor提供了多個(gè)等待策略的實(shí)現(xiàn)。1. BusySpinWaitStrategy:自旋等待,類似Linux Kernel使用的自旋鎖。低延遲但同時(shí)對(duì)CPU資源的占用也多。2. BlockingWaitStrategy :使用鎖和條件變量。CPU資源的占用少,延遲大。3. SleepingWaitStrategy :在多次循環(huán)嘗試不成功后,選擇讓出CPU,等待下次調(diào)度,多次調(diào)度后仍不成功,嘗試前睡眠一個(gè)納秒級(jí)別的時(shí)間再嘗試。這種策略平衡了延遲和CPU資源占用,但延遲不均勻。5. YieldingWaitStrategy :在多次循環(huán)嘗試不成功后,選擇讓出CPU,等待下次調(diào)度。平衡了延遲和CPU資源占用,但延遲比較均勻。6. PhasedBackoffWaitStrategy :上面多種策略的綜合,CPU資源的占用少,延遲大。

  • Event:數(shù)據(jù)從生產(chǎn)者傳遞給消費(fèi)者的數(shù)據(jù)單元。

  • EventProcessor:處理Disruptor中的events的主事件循環(huán),擁有消費(fèi)者Sequence的所有權(quán)。其中BatchEventProcessor即實(shí)現(xiàn)了有效率的event loop,而且可以回調(diào)給實(shí)現(xiàn)了EventHandler接口的類。

  • EventHandler:Disruptor 定義的事件處理接口,由用戶實(shí)現(xiàn),用于處理事件,是Consumer的真正實(shí)現(xiàn)。

  • Producer:即生產(chǎn)者,只是泛指調(diào)用 Disruptor 發(fā)布事件的用戶代碼,Disruptor 沒有定義特定接口或類型。

將這些元素放入Disruptor的context?中,Disruptor的整體結(jié)構(gòu)圖如下:

移動(dòng)開發(fā)培訓(xùn),Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),手機(jī)維修培訓(xùn),手機(jī)軟件培訓(xùn)

多播事件

Queue和Disruptor之間最大的差異。當(dāng)有多個(gè)消費(fèi)者監(jiān)聽在同一Disruptor的所有事件,一個(gè)單一的事件只會(huì)被發(fā)送到一個(gè)單一的消費(fèi)者。Disruptor一個(gè)使用的case是當(dāng)你需要對(duì)同樣的數(shù)據(jù)進(jìn)行不一樣的操作的時(shí)候。LMAX典型的例子是,我們有三個(gè)操作,日志(輸入數(shù)據(jù)寫入持久性日志文件),復(fù)制(將輸入數(shù)據(jù)發(fā)送到另一臺(tái)機(jī)器以確保有數(shù)據(jù)的遠(yuǎn)程復(fù)制),和業(yè)務(wù)邏輯(實(shí)際處理工作)。普通的Executor-style處理,可能是利用WorkPool并行的來處理這些不同的事件。這樣卻不是實(shí)現(xiàn)這個(gè)目標(biāo)最有效的途徑。

如上圖所示,我們有三個(gè)EventHandler(JournalConsumer, ReplicationConsumer and ApplicationConsumer)監(jiān)聽著Disruptor,每一個(gè)Handler都會(huì)順序的收到Disruptor里所有可用的消息,這樣就使得這些消費(fèi)者可以并行的處理這些消息了。

為了支持現(xiàn)實(shí)中并行處理的應(yīng)用,必須支持消費(fèi)者之間的協(xié)調(diào)。回到上面的例子,防止業(yè)務(wù)邏輯的消費(fèi)還在繼續(xù),日志和復(fù)制的消費(fèi)者已經(jīng)完成了他們的任務(wù)是必須的。我們把這個(gè)概念稱為門,或者更準(zhǔn)確地說,這個(gè)行為的超級(jí)集合的特征叫做門。門發(fā)生在兩個(gè)地方。首先,我們需要確保生產(chǎn)者不超過消費(fèi)者。這是通過添加有關(guān)消費(fèi)者到Disruptor時(shí)通過調(diào)用RingBuffer.addgatingconsumers() 實(shí)現(xiàn)的。其次,通過實(shí)現(xiàn)一個(gè)SequenceBarrier(內(nèi)存屏障)的結(jié)構(gòu)可以實(shí)現(xiàn)必須先完成某些操作的需求。

參考圖1,有三個(gè)消費(fèi)者監(jiān)聽喚醒隊(duì)列中的事件,在圖中有一個(gè)依賴圖,ApplicationConsumer依賴于 JournalConsumer 和 ReplicationConsumer,這就說明 JournalConsumer 和 ReplicationConsumer可以互相自由的并發(fā),這層依賴關(guān)系可以從 ApplicationConsumer的 SequenceBarrier連接到 JournalConsumer和 ReplicationConsumer的 Sequences看出來。值得注意的是 Sequencer和下游消費(fèi)者之間的關(guān)系。作用之一就是確保發(fā)布不會(huì)覆蓋Ring Buffer。為了做到這一點(diǎn),下游消費(fèi)者沒有一個(gè)序列比RingBuffer的Sequence還要小,比RingBuffer的size還要小,然而,利用這個(gè)依賴圖可以做一些有意思的操作,因?yàn)锳pplicationConsumers Sequence是小于JournalConsumer 和 ReplicationConsumer(這就是依賴圖所保證的),Sequencer只用關(guān)注ApplicationConsumer的Sequence即可,其實(shí)一般意義上,Sequencer只用知道消費(fèi)者的Sequences??依賴樹中的葉子節(jié)點(diǎn)即可。

事件預(yù)分配

Disruptor的設(shè)計(jì)的一個(gè)目標(biāo)就是能被用在一個(gè)低延遲的環(huán)境中。在低延遲系統(tǒng)中,必須減少或移除內(nèi)存分配操作,基于Java開發(fā)的目的就是減少垃圾回收。(在低延遲的C/C++系統(tǒng)中,大內(nèi)存分配也存在問題,因?yàn)閮?nèi)存分配器也會(huì)存在競(jìng)爭(zhēng))

為了實(shí)現(xiàn)低延遲,Disruptor允許用戶對(duì)事件的內(nèi)存進(jìn)行預(yù)分配,在構(gòu)造過程和用戶提供的EventFactory中都會(huì)在Disruptor 的 RingBuffer中為每個(gè)實(shí)體分配。當(dāng)發(fā)布新數(shù)據(jù)到Disruptor中,API就會(huì)允許用戶獲取構(gòu)造方法的對(duì)象,以至于可以調(diào)用方法或者更新字段。Disruptor對(duì)這些操作提供并發(fā)安全性的保障。

可選的無鎖操作

另一個(gè)關(guān)鍵的實(shí)現(xiàn)低延遲的細(xì)節(jié)就是在Disruptor中利用無鎖的算法,所有內(nèi)存的可見性和正確性都是利用內(nèi)存屏障或者CAS操作。使用CAS來保證多線程安全,與大部分并發(fā)隊(duì)列使用的鎖相比,CAS顯然要快很多。CAS是CPU級(jí)別的指令,更加輕量,不必像鎖一樣需要操作系統(tǒng)提供支持,所以每次調(diào)用不需要在用戶態(tài)與內(nèi)核態(tài)之間切換,也不需要上下文切換。

只有一個(gè)用例中鎖是必須的,那就是BlockingWaitStrategy(阻塞等待策略),唯一的實(shí)現(xiàn)方法就是使用Condition實(shí)現(xiàn)消費(fèi)者在新事件到來前等待。許多低延遲系統(tǒng)使用忙等待去避免Condition的抖動(dòng),然而在系統(tǒng)忙等待的操作中,性能可能會(huì)顯著降低,尤其是在CPU資源嚴(yán)重受限的情況下,例如虛擬環(huán)境下的WEB服務(wù)器。

參考資料:

http://www.cnblogs.com/aheizi/p/6883062.html