LMAX的Disruptor如何工作?(stackoverflow的回答)


原文地址http://stackoverflow.com/questions/6559308/how-does-lmaxs-disruptor-pattern-work

第一個回答(answered Jul 3 '11 at 8:03 Michael Barker):

Disruptor最簡單的描述就是:它是線程間通信最高效的方式。它可以用來替代隊列,同時有很多SEDA和Actors模式的特性。
和隊列比較:
Disruptor可以向其他線程發送消息,並在需要的時候喚醒其他線程(和BlockingQueue相似)。不過,他們之間有三個主要的區別。
1. 使用者通過繼承Entry類並提供一個做預分配之用的工廠來定義消息如何存儲。這樣可以重復利用(復制)內存,或者所實現的Entry可以持有其他對象的引用。
2. 把消息放入Disruptor需要2個步驟,首先在ring buffer中聲名一個接口,這個接口提供使用者一個可以存放正確數據的Entry。然后必須提交這個entry,要想像上文提到的那樣靈活使用內存,這兩個步驟是必需的。提交操作使得消費者線程可以讀取消息。
3. ring buffer中被消費的消息應該由消費者來追蹤。不讓ring buffer來追蹤消息可以減少寫沖突的出現,因為每個線程都自己維護計數器。
與Actors比較
Actor模型是最接近Disruptor的程序模型,尤其是使用BatchConsumer/BatchHandler類。這些類隱藏了所有保持已消費的序列號的復雜實現的並且在重要事情發生的時候會提供一些簡單的回調方法。不過有兩個小小的區別。
1. Disruptor使用一個線程對應一個消費者的模型,而Actors使用多對多的模型,比如你可以擁有盡可能多的actor,它們會分布在一定數量的線程上(一般一個核心一個actor)。
2. BatchHandler接口提供了一個額外的(而且是很重要的)回調方法onEndOfBatch().它允許耗時操作,比如將I/O操作放到批處理中一起執行以提高吞吐量。使用其他Actor框架也可以作批處理,但是它們幾乎都沒有提供批處理執行結束的回調方法,你只能使用超時來判斷批處理是否結束,從而導致差的延時。
和SEDA比較
LMAX設計Disruptor模式是為了替代SEDA。
1. 和SEDA相比,disruptor主要的改進就是可以並行工作。Disruptor支持組播消息來實現這個功能,相同的消息(以一致的順序)被發送給多個消費者。這樣可以避免在管道中交叉層。
2. 消費者可以依賴其他消費者的處理結果,通過把一個隊列化的層放在它們之間。一個消費者可以簡單地看到自己所依賴的消費者的序列號。這樣可以避免在管道中合並層。
和內存障比較
可以把dispruptor理解為一個結構化的,有序的內存障。其中生產者障礙相當於寫障礙,消費者障相當於讀障礙。

 

第二個回答(answered Jul 16 '11 at 5:48 irreputable):

首先我們來理解一下它提供的編程模型。
它有一個或多個作者和讀者。有一排從舊到新的條目(從左到右)。作者可以在右側新增條目。每個讀者按從左到右的順序讀取條目。讀者顯然不能跳過作者先讀取。
條目不能被刪除。我用“讀者”代替“消費者”來避免讓人以為條目會被消費掉。不過我們知道最后一個讀者左邊的條目是沒有用處的。
通常讀者可以並發地獨立地讀取條目。但是我們可以聲明讀者之間的依賴關系。讀者間可以有任意非環形依賴。如果讀者B依賴於讀者A,讀者B不能跳過讀者A先讀取。
讀者A可以注解一個條目,而讀者B依賴於這個注解,所以就有了讀者間的依賴。例如,A在一個條目上做一些計算,然后將結果保存到條目中的a字段。然后A前進,之后B可以讀取這個條目和條目中的a。如果讀者C沒有依賴於A,那么C就不應該讀取a。
這的確是一個有趣的編程模型。不管性能如何,單單這個模型就對應用很有好處。
當然了,LMAX的主要目標就是性能。Disruptor使用一個預分配的條目環。這個環足夠大,但是有上限,從而不會超出容量。如果環滿了,作者會一直等待,直到最慢的作者前進而騰出空間。
條目對象是預分配的並且會一直存在,以減少GC的消耗。我們不會增加新的條目對象或是刪除舊的,相反的,作者會請求一個已存在的條目,填充它的字段,然后通知讀者。這兩個步驟真的是很簡單的原子操作。

setNewEntry(EntryPopulator);
interface EntryPopulator{ void populate(Entry existingEntry); }

預分配的條目相當於adjacent memory cells中的adjacent entries分配(很像),並且因為讀者是順序讀取條目,所以利用CPU緩存很重要。
還有做了很多努力來避免鎖,CAS,甚至是內存障(比如如果只有一個作者的話就使用不變的序列變量)。
寫給開發者:不同注解的讀者應該寫到條目中不同的字段,從而避免寫沖突。(事實上他們應該寫在不同的緩存行中。)一個特定注解的讀者不應該去碰無依賴的讀者可能讀到的任何東西。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM