Disruptor系列(一)— disruptor介紹

本文翻譯自Disruptor在github上的wiki文章Introduction,原文能夠看這裏。git


一.前言

做爲程序猿大多數都有對技術的執着,想在這個方面有所提高。對於優秀的事物保持積極學習的心態,併發編程是開發中一大難題,不管是底層的各類理論仍是上層的各類關於併發組件的實現,都很是的晦澀難懂。併發之因此難,就是由於"多"而難以控制。大多數都會使用"鎖"這種技術進行控制,可是"鎖"這種技術每每和性能又是背道而馳。爲了可以將性能最大化,無鎖 去鎖顯然是提高併發性能的關鍵。前人的智慧還真是慧深莫測,還真在這塊領域有所突破,用一種不一樣以往的設計方式實現了無鎖的高性能併發隊列 — disruptorgithub

本系列就對disruptor進行學習:算法

  • diruptor介紹
  • disruptor使用
  • disruptor原理
  • disruptor在開源中的使用

系列將從以上幾個方面由淺入深,層層遞進式學習。disruptor是open source的,源代碼被託管在github上LMAX-Exchange/disruptor編程

其中wiki有不少很是優秀的文章介紹,是很是不錯的學習資源。但都是英文,索性這裏將其介紹篇使用篇翻譯一遍。安全


二.是什麼

LMAX Disruptor是高性能的線程內部通訊的消息庫。它源於LMAX對併發、性能、非阻塞算法的研究,目前已經成爲LMAX的交易基礎設施的核心部分。服務器

理解Disruptor是什麼的最好方式就是將其與現有的比較好理解的東西比較。Disruptor就至關於Java中BlockingQueue。同隊列同樣,Disruptor的目的就是在同一進程的線程間進行數據交互。然而Disruptor又提供了一些關鍵的不一樣於隊列的特徵:併發

  • 廣播事件至消費者,而且能遵循消費者依賴關係
  • 爲事件預分配內存
  • 可選擇的無鎖


三.核心概念

在理解Disruptor如何工做以前,定義一些廣泛存在文檔和源代碼中的術語是很是有價值的。對於傾向DDD的人而言,它們就是Disruptor領域的無處不在的語言。性能

  • Ring Buffer(環形緩衝):RingBuffer是Disruptor的主要概念。但從Diruptor 3.0開始,RingBuffer只負責存儲和更新Disruptor的數據。一些高級的使用場景,能夠被用戶替換。學習

  • Sequence(序列):Disruptor使用Sequence來標記特定組件到達什麼位置的。每一個消費者(EventProcessor)內部都維護一個Sequence來標記本身消費到的位置。大多數併發代碼都是依賴於Sequence的移動,所以Sequence支持大量AtomicLong的特徵。實際上二者之間的不一樣在於Sequence實現了額外的阻止僞共享的功能。優化

  • Sequencer(序列器):Sequencer是Disruptor中的實際核心。其中兩個實現(Single producer,multi producer)全都實現了在生產者與消費者間進行快速正確的傳遞數據的算法。

  • Sequence Barrier(sequence屏障):Sequence Barrier由Sequencer建立,它包含了來自Sequencer的已經發布的主要sequence的引用,或者包含了依賴的消費者的sequence。同時也包含決定是否有時間可達共消費者處理的邏輯。

  • Wait Strategy(等待策略):Wait Strategy決定了消費者以何種方式等待生產者將事件放進Disruptor。

  • Event(事件):從生產者傳到消費者的數據單元。Event一般由用戶定義,沒有特定的代碼規約。
  • Event Processor(事件處理器):處理來自Disruptor的事件的主要事件循環,且包含了消費者的sequence。有一個實現爲BatchEventProcessor,包含了高效的事件循環,將不斷回調被提供的EventHandler接口。

  • EventHandler(事件處理邏輯):一個接口,由用戶實現定義消費者的處理邏輯。

  • Producer(生產者):這也是用於實現的用戶代碼,調用Disruptor入隊事件。沒有任何代碼規約。

爲了可以將這些概念關聯起來,即放在一個上下文中表示,下圖是個例子,展現LMAX在高性能的核心服務中使用Disruptor的場景:

Note:
原文中沒有對這張圖進行解釋,筆者看到時一臉懵逼,也是在全局的瞭解了Disruptor後再來研究這張圖,才弄明白其含義。這張圖可謂是將Disruptor描繪的淋漓盡致。
這裏筆者也不對其作過多的介紹,由於要弄懂這張圖,勢必要對Disruptor有個總體理解。後面介紹原理時再細緻解析。
總體而言,Producer生產事件放入RingBuffer中,Consumer利用Sequence Barrier和自身包含的Sequence從RingBuffer中獲取可消費的事件。


四.特徵

1.廣播事件

這是隊列和Disruptor之間的一個巨大的行爲差別。當你有多個消費者監聽在相同的Disruptor上是,全部的事件將都被髮送給全部的消費者,這點與隊列不一樣,在隊列中,一個時間將只發送給一個消費者。Disruptor更偏向用在當有多個無依賴的消費者並行處理相同數據的場景中。在LMAX中有個很是經典的案例,有三個操做journalling(寫輸入的數據到一個持久化的日誌文件),replication(發送輸入數據到另外一臺機器,確保遠程備份)和業務邏輯處理。相似Executor風格的事件處理,在這裏同時並行的處理不一樣的事件,可使用WorkePool。

再看上圖,有三個事件處理器(JournalConsumer, ReplicationConsumer和 ApplicationConsumer)監聽Disruptor,每個都將接受Disruptor中全部的可用消息。這個容許三個消費者並行的工做。

2.消費者依賴圖

爲了支持並行處理行爲的實際應用,支持消費者以前的協調是頗有必要的。再以上述例子來講,業務邏輯的處理必須在journalling和replication完成以後。咱們把這種概念叫作gating,或者更準確的說這個行爲特徵的超集被稱爲gating。Disruptor中,Gating發生在兩個地方。第一,咱們須要確保生產者不要超過消費者。經過調用RingBuffer.addGatingConsumers()增長相關的消費者至Disruptor來完成。第二,就是以前所說的場景,經過構造包含須要必須先完成的消費者的Sequence的SequenceBarrier來實現。

引用上圖來講,有三個消費者監聽來自RingBuffer的事件。在這個例子中,有一個依賴關係圖。ApplicationConsumer依賴JournalConsumer和ReplicationConsumer。這個意味着JournalConsumer和ReplicationConsumer能夠自由的併發運行。依賴關係能夠當作是從ApplicationConsumer的SequenceBarrier到JournalConsumer和ReplicationConsumer的Sequence的鏈接。還有一點值得關注,Sequencer與下游的消費者之間的關係。它的角色是確保發佈不會包裹RingBuffer。爲了作到這點,下游消費者的Sequence沒有一個是低於RingBuffer的Sequence而不是RingBuffer的大小。而後使用依賴關係時,一個有趣的優化可使用。由於ApplicationConsumers的Sequence被保證是低於或者等於JournalConsumer和ReplicationConsumer的Sequence,因此Sequencer只須要檢查ApplicationConsumers的Sequence。在更爲廣泛的應用場景中,Sequencer只須要意識到消費者樹中的葉子節點的的Sequence便可。

3.事件預分配

Disruptor的一個目標之一是被用在低延遲的環境中。在低延遲系統中,必需要減小或者去除內存分配。在基於Java的系統中,須要減小因爲GC致使的停頓次數(在低延遲的C/C++系統中,因爲內存分配器的爭用,大量的內存分配也會致使問題)。

爲了知足這點,用戶能夠在Disruptor中爲事件預分配內存。在構造期間,EventFactory由用戶提供,並將在Disruptor的RingBuffer中爲每一個條目調用。當發佈新的數據至Disruptor時,API容許用戶獲取已經被構造的對象,以即可以調用方法或者更新在該對象的域。Disruptor將確保這些操做是線程安全。

4.可選擇的無鎖

對於低延遲的需求又推進了另外一個關鍵性是普遍的使用無鎖算法實現Disruptor。全部的內存可見性和正確性都使用內存屏障和CAS操做實現。只僅僅一個場景BlockingWaitStrategy中使用到了lock。這僅僅是爲了使用條件,以便在等待新事件到達時停放消耗線程。許多低延遲系統都使用忙等來避免使用Condition形成的抖動。可是忙等的數量將會致使性能的降低,特別是CPU資源嚴重受限的狀況下。例如,在虛擬環境中的Web服務器。

相關文章
相關標籤/搜索