閱讀913 返回首頁    go 阿裏雲 go 技術社區[雲棲]


簡單了解Disruptor(一)

1.   Disruptor是什麼

1.1   技術背景

LMAX是在英國注冊並受到FCA監管(監管號碼為509778)的外匯黃金交易所, LMAX架構是LMAX內部研發並應用到交易係統的一種技術。它之所以引起人們的關注,是因為它是一個非常高性能係統,這個係統是建立在JVM平台上,核心是一個業務邏輯處理器,官方號稱它能夠在一個線程裏每秒處理6百萬訂單.

一個僅僅部署在4台服務器上的服務,每秒向Database寫入數據超過100萬行數據,每分鍾產生超過1G的數據。而每台服務器(8核12G)上CPU占用不到100%,load不超過5。

1.2   對比阻塞隊列

 可以和BlockingQueue做對比,不過disruptor除了能完成同樣的工作場景外,能做更多的事,效率也更高。業務邏輯處理器完全是運行在內存中(in-memory),使用事件源驅動方式(event sourcing). 業務邏輯處理器的核心是Disruptors,這是一個並發組件,能夠在無鎖的情況下實現網絡的Queue並發操作。LMAX的研究表明,現在的所謂高性能研究方向似乎和現代CPU設計是相左的。

The disruptor component provides asynchronous SEDA behavior much as the standard SEDA Component, but utilizes a Disruptor instead of a BlockingQueue utilized by the standard SEDA。

 

舉例部分典型的使用方式:

說明: 2.0版本之後,Consumer的概念被EventProcessor代替,都是類似事件處理消費。

P=Producer, EP=Event processor,下麵的模型中p代表了產生數據結構Event,EP代表消費數據Event,消費的過程中做邏輯處理。

* UniCast a series of items between 1 publisher and 1 event processor.
*
* +----+    +-----+
* | P1 |--->| EP1 |
* +----+    +-----+

* Produce an event replicated to two event processors and fold back to a single third event processor.
*
*           +-----+
*    +----->| EP1 |------+
*    |      +-----+      |
*    |                   v
* +----+              +-----+
* | P1 |              | EP3 |
* +----+              +-----+
*    |                   ^
*    |      +-----+      |
*    +----->| EP2 |------+
*           +-----+

* Pipeline a series of stages from a publisher to ultimate event processor.
* Each event processor depends on the output of the event processor.
*
* +----+    +-----+    +-----+    +-----+
* | P1 |--->| EP1 |--->| EP2 |--->| EP3 |
* +----+    +-----+    +-----+    +-----+

* MultiCast a series of items between 1 publisher and 3 event processors.
*
*           +-----+
*    +----->| EP1 |
*    |      +-----+
*    |
* +----+    +-----+
* | P1 |--->| EP2 |
* +----+    +-----+
*    |
*    |      +-----+
*    +----->| EP3 |
*           +-----+

* Sequence a series of events from multiple publishers going to one event processor.
*
* +----+
* | P1 |------+
* +----+      |
*             v
* +----+    +-----+
* | P1 |--->| EP1 |
* +----+    +-----+
*             ^
* +----+      |
* | P3 |------+
* +----+

....

官方代碼examples有更多詳細示例。

 

 

下麵是官方給出的和ArrayBlockingQueue對比測試結果:

  Nehalem 2.8Ghz – Windows 7 SP1 64-bit Sandy Bridge 2.2Ghz – Linux 2.6.38 64-bit
  ABQ Disruptor ABQ Disruptor
Unicast: 1P – 1C 5,339,256 25,998,336 4,057,453 22,381,378
Pipeline: 1P – 3C 2,128,918 16,806,157 2,006,903 15,857,913
Sequencer: 3P – 1C 5,539,531 13,403,268 2,056,118 14,540,519
Multicast: 1P – 3C 1,077,384 9,377,871 260,733 10,860,121
Diamond: 1P – 3C 2,113,941 16,143,613 2,082,725 15,295,197

Comparative throughput (in ops per sec)

 

1.3   Disruptor構成

先介紹幾個相關的核心概念。

  • 環形隊列ringbuffer

數據緩衝區,不同線程之間傳遞數據的BUFFER。RingBuffer是存儲消息的地方,通過一個名為cursor的Sequence對象指示隊列的頭,協調多個生產者向RingBuffer中添加消息,並用於在消費者端判斷RingBuffer是否為空。巧妙的是,表示隊列尾的Sequence並沒有在RingBuffer中,而是由消費者維護。這樣的好處是多個消費者處理消息的方式更加靈活,可以在一個RingBuffer上實現消息的單播,多播,流水線以及它們的組合。在RingBuffer中維護了一個名為gatingSequences的Sequence數組來跟蹤相關Seqence。

  • Producer/Consumer

Producer即生產者,比如下圖中的P1. 隻是泛指調用 Disruptor 發布事件(我們把寫入緩衝隊列的一個元素定義為一個事件)的用戶代碼。

Consumer和EventProcessor是一個概念,新的版本中由EventProcessor概念替代了Consumer

有兩種實現策略,一個是SingleThreadedStrategy(單線程策略)另一個是 MultiThreadedStrategy(多線程策略),兩種策略對應的實現類為SingleProducerSequencer、MultiProducerSequencer【都實現了Sequencer類,之所以叫Sequencer是因為他們都是通過Sequence來實現數據寫,Sequence的概念參見③】 ,它們定義在生產者和消費者之間快速、正確地傳遞數據的並發算法。具體使用哪個根據自己的場景來定,[多線程的策略使用了AtomicLong(Java提供的CAS操作),而單線程的使用long,沒有鎖也沒有CAS。這意味著單線程版本會非常快,因為它隻有一個生產者,不會產生序號上的衝突]

Producer生產event數據,EventHandler作為消費者消費event並進行邏輯處理。消費消息的進度通過Sequence來控製。

③Sequence

Sequence是一個遞增的序號,說白了就是計數器;其次,由於需要在線程間共享,所以Sequence是引用傳遞,並且是線程安全的;再次,Sequence支持CAS操作;最後,為了提高效率,Sequence通過padding來避免偽共享,關於解決偽共享的問題,可以參見下麵章節詳細的介紹。

通過順序遞增的序號來編號管理通過其進行交換的數據(事件),對數據(事件)的處理過程總是沿著序號逐個遞增處理。一個 Sequence 用於跟蹤標識某個特定的事件處理者( RingBuffer/Consumer )的處理進度。生產者對RingBuffer的互斥訪問,生產者與消費者之間的協調以及消費者之間的協調,都是通過Sequence實現。幾乎每一個重要的組件都包含Sequence。

說明:雖然一個 AtomicLong 也可以用於標識進度,但定義 Sequence 來負責該問題還有另一個目的,那就是防止不同的 Sequence 之間的CPU緩存偽共享(Flase Sharing)問題。

 

④Sequence Barrier

用於保持對RingBuffer的 main published Sequence 和Consumer依賴的其它Consumer的 Sequence 的引用。 Sequence Barrier 還定義了決定 Consumer 是否還有可處理的事件的邏輯。SequenceBarrier用來在消費者之間以及消費者和RingBuffer之間建立依賴關係。在Disruptor中,依賴關係實際上指的是Sequence的大小關係,消費者A依賴於消費者B指的是消費者A的Sequence一定要小於等於消費者B的Sequence,這種大小關係決定了處理某個消息的先後順序。因為所有消費者都依賴於RingBuffer,所以消費者的Sequence一定小於等於RingBuffer中名為cursor的Sequence,即消息一定是先被生產者放到Ringbuffer中,然後才能被消費者處理。不好理解的話,可以看下麵章節事例配合了解。

SequenceBarrier在初始化的時候會收集需要依賴的組件的Sequence,RingBuffer的cursor會被自動的加入其中。需要依賴其他消費者和/或RingBuffer的消費者在消費下一個消息時,會先等待在SequenceBarrier上,直到所有被依賴的消費者和RingBuffer的Sequence大於等於這個消費者的Sequence。當被依賴的消費者或RingBuffer的Sequence有變化時,會通知SequenceBarrier喚醒等待在它上麵的消費者。

 

⑤Wait Strategy

當消費者等待在SequenceBarrier上時,有許多可選的等待策略,不同的等待策略在延遲和CPU資源的占用上有所不同,可以視應用場景選擇:

  • BusySpinWaitStrategy : 自旋等待,類似Linux Kernel使用的自旋鎖。低延遲但同時對CPU資源的占用也多。
  • BlockingWaitStrategy : 使用鎖和條件變量。CPU資源的占用少,延遲大。
  • SleepingWaitStrategy : 在多次循環嚐試不成功後,選擇讓出CPU,等待下次調度,多次調度後仍不成功,嚐試前睡眠一個納秒級別的時間再嚐試。這種策略平衡了延遲和CPU資源占用,但延遲不均勻。
  • YieldingWaitStrategy : 在多次循環嚐試不成功後,選擇讓出CPU,等待下次調。平衡了延遲和CPU資源占用,但延遲也比較均勻。
  • PhasedBackoffWaitStrategy : 上麵多種策略的綜合,CPU資源的占用少,延遲大。

 

 

  • Event

在 Disruptor 的語義中,生產者和消費者之間進行交換的數據被稱為事件(Event)。它不是一個被 Disruptor 定義的特定類型,而是由 Disruptor 的使用者定義並指定。

  • EventProcessor

EventProcessor 持有特定消費者(Consumer)的 Sequence,並提供用於調用事件處理實現的事件循環(Event Loop)。通過把EventProcessor提交到線程池來真正執行,有兩類Processor:

其中一類消費者是BatchEvenProcessor。每個BatchEvenProcessor有一個Sequence,來記錄自己消費RingBuffer中消息的情況。所以,一個消息必然會被每一個BatchEvenProcessor消費。

另一類消費者是WorkProcessor。每個WorkProcessor也有一個Sequence,多個WorkProcessor還共享一個Sequence用於互斥的訪問RingBuffer。一個消息被一個WorkProcessor消費,就不會被共享一個Sequence的其他WorkProcessor消費。這個被WorkProcessor共享的Sequence相當於尾指針

 

  • EventHandler

Disruptor 定義的事件處理接口,由用戶實現,用於處理事件,是 Consumer 的真正實現。開發者實現EventHandler,然後作為入參傳遞給EventProcessor的實例。

綜上所述,附官方類圖:

 轉載自 並發編程網 - ifeve.com

最後更新:2017-05-19 10:01:42

  上一篇:go  Springboot 實現 Restful 服務,基於 HTTP / JSON 傳輸
  下一篇:go  《雲周刊》第121期:圖管夠!灌籃高手、女兒國…阿裏日,這幫程序員太會玩了!