Disruptor是一個由LMAX開源的Java併發框架。LMAX是一種新型零售金融交易平臺,這個系統是創建在 JVM 平臺上,核心是一個業務邏輯處理器,它可以在一個線程裏每秒處理 6 百萬訂單。業務邏輯處理器徹底是運行在內存中(in-memory),使用事件源驅動方式(event sourcing),具備低延遲,高吞吐的特性。html
disruptor有多快?官方給出了和ArrayBlockingQueue的比較圖表:git
Disruptor能夠用來解決併發編程中的一個廣泛的問題: 消息隊列的處理(producer和consumer)。github
Disruptor 相對於傳統方式的優勢:算法
在瞭解disruptor如何工做以前,咱們先看一下disruptor一些重要組件的介紹(翻譯自官方文檔,略有修改):編程
將這些元素放入Disruptor的context中,Disruptor的總體結構圖以下:數組
多播事件緩存
Queue和Disruptor之間最大的差別。當有多個消費者監聽在同一Disruptor的全部事件,一個單一的事件只會被髮送到一個單一的消費者。Disruptor一個使用的case是當你須要對一樣的數據進行不同的操做的時候。LMAX典型的例子是,咱們有三個操做,日誌(輸入數據寫入持久性日誌文件),複製(將輸入數據發送到另外一臺機器以確保有數據的遠程複製),和業務邏輯(實際處理工做)。普通的Executor-style處理,多是利用WorkPool並行的來處理這些不一樣的事件。這樣卻不是實現這個目標最有效的途徑。安全
如上圖所示,咱們有三個EventHandler(JournalConsumer, ReplicationConsumer and ApplicationConsumer)監聽着Disruptor,每個Handler都會順序的收到Disruptor裏全部可用的消息,這樣就使得這些消費者能夠並行的處理這些消息了。性能優化
爲了支持現實中並行處理的應用,必須支持消費者之間的協調。回到上面的例子,防止業務邏輯的消費還在繼續,日誌和複製的消費者已經完成了他們的任務是必須的。咱們把這個概念稱爲門,或者更準確地說,這個行爲的超級集合的特徵叫作門。門發生在兩個地方。首先,咱們須要確保生產者不超過消費者。這是經過添加有關消費者到Disruptor時經過調用RingBuffer.addgatingconsumers() 實現的。其次,經過實現一個SequenceBarrier(內存屏障)的結構能夠實現必須先完成某些操做的需求。服務器
參考圖1,有三個消費者監聽喚醒隊列中的事件,在圖中有一個依賴圖,ApplicationConsumer依賴於 JournalConsumer 和 ReplicationConsumer,這就說明 JournalConsumer 和 ReplicationConsumer能夠互相自由的併發,這層依賴關係能夠從 ApplicationConsumer的 SequenceBarrier鏈接到 JournalConsumer和 ReplicationConsumer的 Sequences看出來。值得注意的是 Sequencer和下游消費者之間的關係。做用之一就是確保發佈不會覆蓋Ring Buffer。爲了作到這一點,下游消費者沒有一個序列比RingBuffer的Sequence還要小,比RingBuffer的size還要小,然而,利用這個依賴圖能夠作一些有意思的操做,由於ApplicationConsumers Sequence是小於JournalConsumer 和 ReplicationConsumer(這就是依賴圖所保證的),Sequencer只用關注ApplicationConsumer的Sequence便可,其實通常意義上,Sequencer只用知道消費者的Sequences依賴樹中的葉子節點便可。
事件預分配
Disruptor的設計的一個目標就是能被用在一個低延遲的環境中。在低延遲系統中,必須減小或移除內存分配操做,基於Java開發的目的就是減小垃圾回收。(在低延遲的C/C++系統中,大內存分配也存在問題,由於內存分配器也會存在競爭)
爲了實現低延遲,Disruptor容許用戶對事件的內存進行預分配,在構造過程和用戶提供的EventFactory中都會在Disruptor 的 RingBuffer中爲每一個實體分配。當發佈新數據到Disruptor中,API就會容許用戶獲取構造方法的對象,以致於能夠調用方法或者更新字段。Disruptor對這些操做提供併發安全性的保障。
可選的無鎖操做
另外一個關鍵的實現低延遲的細節就是在Disruptor中利用無鎖的算法,全部內存的可見性和正確性都是利用內存屏障或者CAS操做。使用CAS來保證多線程安全,與大部分併發隊列使用的鎖相比,CAS顯然要快不少。CAS是CPU級別的指令,更加輕量,沒必要像鎖同樣須要操做系統提供支持,因此每次調用不須要在用戶態與內核態之間切換,也不須要上下文切換。
只有一個用例中鎖是必須的,那就是BlockingWaitStrategy(阻塞等待策略),惟一的實現方法就是使用Condition實現消費者在新事件到來前等待。許多低延遲系統使用忙等待去避免Condition的抖動,然而在系統忙等待的操做中,性能可能會顯著下降,尤爲是在CPU資源嚴重受限的狀況下,例如虛擬環境下的WEB服務器。
參考資料:
LMAX Disruptor
Spark性能優化指南——基礎篇- - 美團點評技術團隊
Disruptor入門