前言
最近工作比較忙,在工作專案中,看了很多人都自己實現了一套資料任務處理機制,個人感覺有點亂,且也方便他人的後續維護,所以想到了一種資料處理模式,即生產者、緩衝佇列、消費者的模式來統一大家的實現邏輯。
下面時是對Disruptor基本使用的演示。使用中需要引入依賴
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
複製程式碼
名稱解釋
Ring Buffer
環境的快取區,3.0版本以前被認為是Disruptor的主要成員。3.0版本以後,環形緩衝區只負責通過Disruptor的事件方式來對資料進行儲存和更新。在一些高階的應用場景中,Ring Buffer可以由使用者的自定義實現完全替代。
Sequence
Disruptor使用Sequence作為一種方法來確定特定元件的位置。每個使用者(EventProcessor)與Disruptor本身一樣維護一個序列。大多數併發程式碼依賴於這些序列值的移動,因此序列支援AtomicLong的許多當前特性。事實上,兩者之間唯一真正的區別是序列包含額外的功能,以防止序列和其他值之間的錯誤共享。
Sequencer
Sequencer是真正的核心,該介面的兩個實現(單生產者, 多消費者)實現了所有用於在生產者和使用者之間的快速、正確的傳遞資料的併發演算法。
Sequence Barrier
序列屏障由Sequencer產生,包含對Sequencer和任何依賴消費者的序列的引用。它包含確定是否有任何事件可供使用者處理的邏輯。
Wait Strategy
等待策略確定消費者將如何等待生產者產生的訊息,Disruptor將訊息放到事件(Event)中。
Event
從生產者到消費者的資料單位。不存在完全由使用者定義的事件的特定程式碼的表示形式。
EventProcessor
EventProcessor持有特定消費者(Consumer)的Sequence,並提供用於呼叫事件處理實現的事件迴圈。
BatchEventProcessor
BatchEventProcessor它包含事件迴圈的有效實現,並將回撥到已使用的EventHandle介面實現。
EventHandler
Disruptor定義的事件處理介面,由使用者實現,用於處理事件,是Consumer的真正實現。
Producer
生產者,只是泛指呼叫Disruptor釋出事件的使用者程式碼,Disruptor沒有定義特定介面或型別。
架構圖
簡單實用Disruptor
1 定義事件
事件就是通過Disruptor進行交換的資料型別。
package com.disruptor;
public class Data {
private long value;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
複製程式碼
2 定義事件工廠
事件工廠定義瞭如何例項化第一步中定義的事件。Disruptor通過EventFactory在RingBuffer中預建立Event的例項。
一個Event例項被用作一個資料槽,釋出者釋出前,先從RingBuffer獲得一個Event的例項,然後往Event例項中插入資料,然後再發布到RingBuffer中,最後由Consumer獲得Event例項並從中讀取資料。
package com.disruptor;
import com.lmax.disruptor.EventFactory;
public class DataFactory implements EventFactory<Data> {
@Override
public Data newInstance() {
return new Data();
}
}
複製程式碼
3 定義生產者
package com.disruptor;
import com.lmax.disruptor.RingBuffer;
import java.nio.ByteBuffer;
public class Producer {
private final RingBuffer<Data> ringBuffer;
public Producer(RingBuffer<Data> ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void pushData(ByteBuffer byteBuffer) {
long sequence = ringBuffer.next();
try {
Data even = ringBuffer.get(sequence);
even.setValue(byteBuffer.getLong(0));
} finally {
ringBuffer.publish(sequence);
}
}
}
複製程式碼
4 定義消費者
package com.disruptor;
import com.lmax.disruptor.WorkHandler;
import java.text.MessageFormat;
public class Consumer implements WorkHandler<Data> {
@Override
public void onEvent(Data data) throws Exception {
long result = data.getValue() + 1;
System.out.println(MessageFormat.format("Data process : {0} + 1 = {1}", data.getValue(), result));
}
}
複製程式碼
5 啟動Disruptor
- 測試Demo
package com.disruptor;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import java.nio.ByteBuffer;
import java.util.concurrent.ThreadFactory;
public class Main {
private static final int NUMS = 10;
private static final int SUM = 1000000;
public static void main(String[] args) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long start = System.currentTimeMillis();
DataFactory factory = new DataFactory();
int buffersize = 1024;
Disruptor<Data> disruptor = new Disruptor<Data>(factory, buffersize, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
});
Consumer[] consumers = new Consumer[NUMS];
for (int i = 0; i < NUMS; i++) {
consumers[i] = new Consumer();
}
disruptor.handleEventsWithWorkerPool(consumers);
disruptor.start();
RingBuffer<Data> ringBuffer = disruptor.getRingBuffer();
Producer producer = new Producer(ringBuffer);
ByteBuffer bb = ByteBuffer.allocate(8);
for (long i = 0; i < SUM; i++) {
bb.putLong(0, i);
producer.pushData(bb);
System.out.println("Success producer data : " + i);
}
long end = System.currentTimeMillis();
disruptor.shutdown();
System.out.println("Total time : " + (end - start));
}
}
複製程式碼
- 結果(部分結果展示)
Data process : 999,987 + 1 = 999,988
Success producer data : 999995
Data process : 999,990 + 1 = 999,991
Data process : 999,989 + 1 = 999,990
Data process : 999,991 + 1 = 999,992
Data process : 999,992 + 1 = 999,993
Data process : 999,993 + 1 = 999,994
Data process : 999,995 + 1 = 999,996
Success producer data : 999996
Success producer data : 999997
Success producer data : 999998
Success producer data : 999999
Data process : 999,994 + 1 = 999,995
Data process : 999,996 + 1 = 999,997
Data process : 999,997 + 1 = 999,998
Data process : 999,998 + 1 = 999,999
Data process : 999,999 + 1 = 1,000,000
Total time : 14202
複製程式碼
由結果展示可見,邊生產、邊消費。
彩蛋
1 事件轉換類
package com.mm.demo.disruptor.translator;
import com.lmax.disruptor.EventTranslatorOneArg;
import com.mm.demo.disruptor.entity.Data;
public class DataEventTranslator implements EventTranslatorOneArg<Data, Long> {
@Override
public void translateTo(Data event, long sequence, Long arg0) {
System.out.println(MessageFormat.format("DataEventTranslator arg0 = {0}, seq = {1}", arg0, sequence));
event.setValue(arg0);
}
}
複製程式碼
2 消費者
2.1 消費者Demo1
消費者每次將event的結果加1。
package com.mm.demo.disruptor.handler;
import com.lmax.disruptor.EventHandler;
import com.mm.demo.disruptor.entity.Data;
import java.text.MessageFormat;
public class D1DataEventHandler implements EventHandler<Data> {
@Override
public void onEvent(Data event, long sequence, boolean endOfBatch) throws Exception {
long result = event.getValue() + 1;
Thread t = new Thread();
String name = t.getName();
System.out.println(MessageFormat.format("consumer "+name+": {0} + 1 = {1}", event.getValue(), result));
}
}
複製程式碼
這裡是使用的是EventHandler。也是使用WorkHandler,EventHandler和WorkHandler的區別是前者不需要池化,後者需要池化。
2.2 消費者Demo2
package com.mm.demo.disruptor.handler;
import com.lmax.disruptor.EventHandler;
import com.mm.demo.disruptor.entity.Data;
import java.text.MessageFormat;
public class D2DataEventHandler implements EventHandler<Data> {
@Override
public void onEvent(Data event, long sequence, boolean endOfBatch) throws Exception {
long result = event.getValue() + 2;
System.out.println(MessageFormat.format("consumer 2: {0} + 2 = {1}", event.getValue(), result));
}
}
複製程式碼
2.3 序列依次計算
Consumer1執行完成再執行Consumer2。
package com.mm.demo.disruptor.process;
import com.lmax.disruptor.dsl.Disruptor;
import com.mm.demo.disruptor.entity.Data;
import com.mm.demo.disruptor.handler.D1DataEventHandler;
import com.mm.demo.disruptor.handler.D2DataEventHandler;
/**
* 序列依次計算
* @DateT: 2020-01-07
*/
public class Serial {
public static void serial(Disruptor<Data> disruptor) {
disruptor.handleEventsWith(new D1DataEventHandler()).then(new D2DataEventHandler());
disruptor.start();
}
}
複製程式碼
2.4 並行實時計算
Consumer1和Consumer2同時執行。
package com.mm.demo.disruptor.process;
import com.lmax.disruptor.dsl.Disruptor;
import com.mm.demo.disruptor.entity.Data;
import com.mm.demo.disruptor.handler.D1DataEventHandler;
import com.mm.demo.disruptor.handler.D2DataEventHandler;
/**
* 並行執行
* @DateT: 2020-01-07
*/
public class Parallel {
public static void parallel(Disruptor<Data> dataDisruptor) {
dataDisruptor.handleEventsWith(new D1DataEventHandler(), new D2DataEventHandler());
dataDisruptor.start();
}
}
複製程式碼
2.5 測試類
package com.mm.demo.disruptor;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.mm.demo.disruptor.entity.Data;
import com.mm.demo.disruptor.handler.D1DataEventHandler;
import com.mm.demo.disruptor.process.Parallel;
import com.mm.demo.disruptor.process.Serial;
import com.mm.demo.disruptor.translator.DataEventTranslator;
import javax.swing.plaf.synth.SynthTextAreaUI;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class Main {
private static final int BUFFER = 1024 * 1024;
public static void main(String[] args) {
DataFactory factory = new DataFactory();
Disruptor<Data> disruptor = new Disruptor<Data>(factory, BUFFER, Executors.defaultThreadFactory(), ProducerType.MULTI, new BlockingWaitStrategy());
Serial.serial(disruptor);
// Parallel.parallel(disruptor);
RingBuffer<Data> ringBuffer = disruptor.getRingBuffer();
for (int i = 0; i < 2; i++) {
ringBuffer.publishEvent(new DataEventTranslator(), (long)i);
}
disruptor.shutdown();
}
}
複製程式碼
總結
上邊只演示了序列和並行的方式,其實還是通過組合的方式建立不的計算處理方式(需要建立多個事件處理器EventHandler)。
補充等待策略
- BlockingWaitStrategy:最低效的策略,但是對cpu的消耗是最小的,在各種不同部署環境中能提供更加一致的效能表現。
- SleepingWaitStrategy:效能和BlockingWaitStrategy差不多少,cpu消耗也類似,但是其對生產者執行緒的影響最小,適合用於非同步處理資料的場景。
- YieldingWaitStrategy:效能是最好的,適用於低延遲的場景。在要求極高效能且事件處理執行緒數小於cpu處理核數時推薦使用此策略。
- BusySpinWaitStrategy:低延遲,但是對cpu資源的佔用較多。
- PhasedBackoffWaitStrategy:上邊幾種策略的綜合體,延遲大,但是佔用cpu資源較少。
參考
本文參考了Disruptor原始碼以及github中的部分說明。
Demo原始碼地址
- 寫作不易,轉載請註明出處,喜歡的小夥伴可以關注公眾號檢視更多喜歡的文章。
- 聯絡方式:4272231@163.com
- QQ:95472323
- 微信:ffj2000