Disruptor併發框架(九)
阿新 • • 發佈:2018-12-20
Disruptor併發框架簡介
- Disruptor是一個開源的併發框架,能夠在無鎖的情況下實現網路的Queue併發操作。
- Disruptor是一個高效能的非同步處理框架,或者可以認為是最快的訊息框架(輕量級JMS),也可以認為是一個觀察者模式的實現,或者事件監聽模式的實現。
Disruptor Hello World
在Disruptor中,進行開發時需要引入相應的jar包。我們實現Hello World需要如下幾個步驟:
- 建立一個Event類。
- 建立一個工廠Event類,用於建立Event類例項物件。
- 需要有一個事件監聽事件類,用於處理資料(Event類)。
- 我們需要進行測試程式碼編寫。例項化Disruptor例項,配置一系列引數。然後我們對Disruptor例項繫結監聽事件類,接收並處理資料。
- 在Disruptor中,真正儲存資料的核心叫做RingBuffer,我們通過Disruptor例項來拿到它,然後把資料生產出來,把資料加入到RingBuffer的例項物件中即可。
public class LongEvent {
private long value;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
/** * 需要讓disruptor為我們建立事件,我們同時還宣告一個EventFactory來例項化Event物件 * */ public class LongEventFactory implements EventFactory{ @Override public Object newInstance() { return new LongEvent(); } }
/**
* 事件消費者:
* 也就是一個事件處理器,這個事件處理器簡單地把事件中儲存的資料列印到終端
* */
public class LongEventHandler implements EventHandler<LongEvent> {
@Override
public void onEvent(LongEvent arg0, long arg1, boolean arg2) throws Exception {
System.out.println(arg0.getValue());
}
}
public class LongEventMain { public static void main(String[] args) { //建立執行緒池 ExecutorService executor = Executors.newCachedThreadPool(); //建立工程 LongEventFactory factory = new LongEventFactory(); //建立bufferSize,也就是RingBuffer大小,必須是2的N次方 int ringBufferSize = 1024 * 1024; /** BlockingWaitStrategy 是最低效的策略,但其對CPU的消耗最小並且在各種不同部署環境中能提供更加一致的效能表現 WaitStrategy BLOCKING_WAIT = new BlockingWaitStrategy(); SleepingWaitStrategy 的效能表現跟BlockingWaitStrategy差不多,對CPU的消耗也類似,但其對生產者執行緒的影響最小,適合用於非同步日誌類似的場景 WaitStrategy SLEEPING_WAIT = new SleepingWaitStrategy(); YieldingWaitStrategy 的效能是最好的,適合用於低延遲的系統。在要求極高效能且事件處理線數小於CPU邏輯核心數的場景中,推薦使用此策略;例如,CPU開啟超執行緒的特性 WaitStrategy YIELDING_WAIT = new YieldingWaitStrategy(); */ /* 建立disruptor * 1.第一個引數為工廠類物件,用於建立一個個的LongEvent,LongEvent是實際的消費資料。 * 2.第二個引數為快取區大小 * 3.第三個引數為執行緒池,進行Disruptor內部的資料接收處理排程 * 4.第四個引數ProducerType.SINGLE(表示一個生產者) 和 ProducerType.MULTI(多個生產者) * 5.第五個引數是一種策略,就是生產和消費的策略 * */ Disruptor<LongEvent> disruptor = new Disruptor<>(factory, ringBufferSize, executor, ProducerType.SINGLE, new YieldingWaitStrategy()); //連線消費事件方法 disruptor.handleEventsWith(new LongEventHandler()); //啟動 disruptor.start(); //Disruptor的事件釋出過程是一個兩階段提交過程 //使用該方法獲得具體存放資料的容器RingBuffer(環形結構) RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer(); LongEventProducer producer = new LongEventProducer(ringBuffer); ByteBuffer byteBuffer = ByteBuffer.allocate(8); for(long input = 0; input < 100; input++){ byteBuffer.putLong(0,input); producer.onData(byteBuffer); } disruptor.shutdown(); //關閉disruptor,方法會阻塞,直到所有的時間得到處理 executor.shutdown(); //關閉executor,disruptor不會自動關閉executor } }
public class LongEventProducer {
private final RingBuffer<LongEvent> ringBuffer;
public LongEventProducer(RingBuffer<LongEvent> ringBuffer){
this.ringBuffer = ringBuffer;
}
/**
* onData用來發布事件,每呼叫一次就釋出一次事件
* 他的引數會用該事件傳遞給消費者
* */
public void onData(ByteBuffer input){
//1.把ringBuffer看做一個事件佇列,那麼next就是得到下一個事件槽
long sequence = ringBuffer.next();
try{
//2.用上面的索引取出一個空的事件用於填充(獲取該序號對應的事件物件)
LongEvent event = ringBuffer.get(sequence);
//3.獲取要通過事件傳遞的業務資料
event.setValue(input.getLong(0));
} finally {
//4.釋出事件
//注意,最後的ringBuffer.publish 方法必須包含在finally中以確保必須得到呼叫
//如果某個請求的sequence未被提交,則對應的消費者獲取不了資料
ringBuffer.publish(sequence);
}
}
}
Disruptor術語說明
- RingBuffer: 被看做Disruptor最主要元件,然而從3.0開始RingBuffer僅僅負責儲存和更新在Disruptor中流通的資料。對一些特殊的使用場景能夠被使用者(其他資料結構)完全替代。
- Sequence: Disruptor使用Sequence來表示一個特殊元件處理的序號。和Disruptor一樣,每個消費者(EventProcessor)都維持著一個Sequence。大部分的併發程式碼依賴這些Sequence值得運轉,因此Sequence支援多種當前為AtomicLong類的特性。
- Sequencer: 這是Disruptor真正的核心。實現了這個介面的兩種生產者(單生產者和多生產者)均實現了所有的併發演算法,為了在生產者和消費者之間進行準確快速的資料傳遞。
- SequenceBarrier: 有Sequence生成,並且包含了已經發布的Sequence的引用,這些Sequence源於Sequenceer和一些獨立的消費者的Sequence。它包含了決定是否有供消費者來消費者的Event的邏輯。
- WaitStrategy: 決定了一個消費者將如何等待生產者將Event置於Disruptor。
- Event: 從生產者到消費者過程中所處理的資料單元。Disruptor中沒有程式碼表示Event,因此它完全是由使用者定義。
- EventProcessor: 主要時間迴圈,處理Disruptor中的Event,並且擁有消費者的Sequence。他有一個實現類是BatchEventProcessor,包含了event loop有效的實現,並且將回調到一個EventHandler介面的實現物件。
- EventHandler: 由使用者實現並且代表了Disruptor中的一個消費者的介面。
- Producer: 由使用者實現,它呼叫RingBuffer來插入事件(Event),在Disruptor中沒有相應的實現程式碼,由使用者實現。
- WorkProcessor: 確保每個sequence只被一個processor消費,在同一個WorkPool中的處理多個WorkProcessor不會消費同樣的sequence。
- WorkerPool: 一個WorkProcessor池,其中WorkProcessor將消費Sequence,所以任務可以在實現WorkHandler介面的worker之間移交。
- LifecycleAware: 當BatchEventProcessor啟動和停止時,實現這個介面用於接收通知。
Disruptor印象
初看Disruptor,給人的印象就是RingBuffer是其和興,生產者向RingBuffer中寫入元素,消費者從RingBuffer中消費元素。
理解RingBuffer
- RingBuffer到底是什麼? 它是一個環(首尾相接的環),可以把它用作在不同上下文(執行緒)間傳遞資料的buffer。RingBuffer擁有一個序號,這個序號(sequence)指向陣列中下一個可用元素。隨著你不停地填充這個buffer(可能會有相應的讀取),這個序號一直增長,直到繞過這個環。要找到陣列中當前序號指向的元素,可以通過mod操作:sequence mod array.length = array.index(取模操作)。如果槽的個數是2的N次方更有利於基於二進位制的計算機計算。
場景使用
在HelloWorld的例項中,我們建立Disruptor例項,然後呼叫getRingBuffer方法區獲取RingBuffer,其實很多時候,我們可以直接使用RingBuffer,以及其他的API操作。
- 使用EventProcessor訊息處理器:
public class Main1 {
public static void main(String[] args) throws Exception {
int BUFFER_SIZE=1024;
int THREAD_NUMBERS=4;
/*
* createSingleProducer建立一個單生產者的RingBuffer,
* 第一個引數叫EventFactory,從名字上理解就是"事件工廠",其實它的職責就是產生資料填充RingBuffer的區塊。
* 第二個引數是RingBuffer的大小,它必須是2的指數倍 目的是為了將求模運算轉為&運算提高效率
* 第三個引數是RingBuffer的生產都在沒有可用區塊的時候(可能是消費者(或者說是事件處理器) 太慢了)的等待策略
*/
final RingBuffer<Trade> ringBuffer = RingBuffer.createSingleProducer(new EventFactory<Trade>() {
@Override
public Trade newInstance() {
return new Trade();
}
}, BUFFER_SIZE, new YieldingWaitStrategy());
//建立執行緒池
ExecutorService executors = Executors.newFixedThreadPool(THREAD_NUMBERS);
//建立SequenceBarrier
SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();
//建立訊息處理器
BatchEventProcessor<Trade> transProcessor = new BatchEventProcessor<Trade>(
ringBuffer, sequenceBarrier, new TradeHandler());
//這一步的目的就是把消費者的位置資訊引用注入到生產者 如果只有一個消費者的情況可以省略
ringBuffer.addGatingSequences(transProcessor.getSequence());
//把訊息處理器提交到執行緒池
executors.submit(transProcessor);
//如果存在多個消費者 那重複執行上面3行程式碼 把TradeHandler換成其它消費者類
Future<?> future= executors.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
long seq;
for(int i=0;i<10;i++){
seq = ringBuffer.next();//佔個坑 --ringBuffer一個可用區塊
ringBuffer.get(seq).setPrice(Math.random()*9999);//給這個區塊放入 資料
ringBuffer.publish(seq);//釋出這個區塊的資料使handler(consumer)可見
}
return null;
}
});
future.get();//等待生產者結束
Thread.sleep(1000);//等上1秒,等消費都處理完成
transProcessor.halt();//通知事件(或者說訊息)處理器 可以結束了(並不是馬上結束!!!)
executors.shutdown();//終止執行緒
}
}
public class TradeHandler implements EventHandler<Trade>, WorkHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
this.onEvent(event);
}
@Override
public void onEvent(Trade event) throws Exception {
//這裡做具體的消費邏輯
event.setId(UUID.randomUUID().toString());//簡單生成下ID
System.out.println(event.getId());
}
}
public class Trade {
private String id;//ID
private String name;
private double price;//金額
private AtomicInteger count = new AtomicInteger(0);
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public AtomicInteger getCount() {
return count;
}
public void setCount(AtomicInteger count) {
this.count = count;
}
}
- 使用WorkerPool訊息處理器:
public class Main2 {
public static void main(String[] args) throws InterruptedException {
int BUFFER_SIZE=1024;
int THREAD_NUMBERS=4;
EventFactory<Trade> eventFactory = new EventFactory<Trade>() {
public Trade newInstance() {
return new Trade();
}
};
RingBuffer<Trade> ringBuffer = RingBuffer.createSingleProducer(eventFactory, BUFFER_SIZE);
SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();
ExecutorService executor = Executors.newFixedThreadPool(THREAD_NUMBERS);
WorkHandler<Trade> handler = new TradeHandler();
WorkerPool<Trade> workerPool = new WorkerPool<Trade>(ringBuffer, sequenceBarrier, new IgnoreExceptionHandler(), handler);
workerPool.start(executor);
//下面這個生產8個數據
for(int i=0;i<8;i++){
long seq=ringBuffer.next();
ringBuffer.get(seq).setPrice(Math.random()*9999);
ringBuffer.publish(seq);
}
Thread.sleep(1000);
workerPool.halt();
executor.shutdown();
}
}
public class TradeHandler implements EventHandler<Trade>, WorkHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
this.onEvent(event);
}
@Override
public void onEvent(Trade event) throws Exception {
//這裡做具體的消費邏輯
event.setId(UUID.randomUUID().toString());//簡單生成下ID
System.out.println(event.getId());
}
}
public class Trade {
private String id;//ID
private String name;
private double price;//金額
private AtomicInteger count = new AtomicInteger(0);
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public AtomicInteger getCount() {
return count;
}
public void setCount(AtomicInteger count) {
this.count = count;
}
}
- 在複雜場景下使用RingBuffer(希望P1生產的資料給C1、C2並行執行,最後C1、C2執行結束後C3執行):
public class Main {
public static void main(String[] args) throws InterruptedException {
long beginTime=System.currentTimeMillis();
int bufferSize=1024;
ExecutorService executor=Executors.newFixedThreadPool(8);
Disruptor<Trade> disruptor = new Disruptor<Trade>(new EventFactory<Trade>() {
@Override
public Trade newInstance() {
return new Trade();
}
}, bufferSize, executor, ProducerType.SINGLE, new BusySpinWaitStrategy());
//菱形操作
/**
//使用disruptor建立消費者組C1,C2
EventHandlerGroup<Trade> handlerGroup =
disruptor.handleEventsWith(new Handler1(), new Handler2());
//宣告在C1,C2完事之後執行JMS訊息傳送操作 也就是流程走到C3
handlerGroup.then(new Handler3());
*/
//順序操作
/**
disruptor.handleEventsWith(new Handler1()).
handleEventsWith(new Handler2()).
handleEventsWith(new Handler3());
*/
//六邊形操作.
/**
Handler1 h1 = new Handler1();
Handler2 h2 = new Handler2();
Handler3 h3 = new Handler3();
Handler4 h4 = new Handler4();
Handler5 h5 = new Handler5();
disruptor.handleEventsWith(h1, h2);
disruptor.after(h1).handleEventsWith(h4);
disruptor.after(h2).handleEventsWith(h5);
disruptor.after(h4, h5).handleEventsWith(h3);
*/
disruptor.start();//啟動
CountDownLatch latch=new CountDownLatch(1);
//生產者準備
executor.submit(new TradePublisher(latch, disruptor));
latch.await();//等待生產者完事.
disruptor.shutdown();
executor.shutdown();
System.out.println("總耗時:"+(System.currentTimeMillis()-beginTime));
}
}
public class Handler1 implements EventHandler<Trade>,WorkHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
this.onEvent(event);
}
@Override
public void onEvent(Trade event) throws Exception {
System.out.println("handler1: set name");
event.setName("h1");
Thread.sleep(1000);
}
}
public class Handler2 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler2: set price");
event.setPrice(17.0);
Thread.sleep(1000);
}
}
public class Handler3 implements EventHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("handler3: name: " + event.getName() + " , price: " + event.getPrice() + "; instance: " + event.toString());
}
}
public class Handler4 implements EventHandler<Trade>,WorkHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
this.onEvent(event);
}
@Override
public void onEvent(Trade event) throws Exception {
System.out.println("handler4: get name : " + event.getName());
event.setName(event.getName() + "h4");
}
}
public class Handler5 implements EventHandler<Trade>,WorkHandler<Trade> {
@Override
public void onEvent(Trade event, long sequence, boolean endOfBatch) throws Exception {
this.onEvent(event);
}
@Override
public void onEvent(Trade event) throws Exception {
System.out.println("handler5: get price : " + event.getPrice());
event.setPrice(event.getPrice() + 3.0);
}
}
public class TradePublisher implements Runnable {
Disruptor<Trade> disruptor;
private CountDownLatch latch;
private static int LOOP=10;//模擬百萬次交易的發生
public TradePublisher(CountDownLatch latch,Disruptor<Trade> disruptor) {
this.disruptor=disruptor;
this.latch=latch;
}
@Override
public void run() {
TradeEventTranslator tradeTransloator = new TradeEventTranslator();
for(int i=0;i<LOOP;i++){
disruptor.publishEvent(tradeTransloator);
}
latch.countDown();
}
}
class TradeEventTranslator implements EventTranslator<Trade>{
private Random random=new Random();
@Override
public void translateTo(Trade event, long sequence) {
this.generateTrade(event);
}
private Trade generateTrade(Trade trade){
trade.setPrice(random.nextDouble()*9999);
return trade;
}
}
- 多生產者和多消費者(複雜場景使用Disruptor,簡單場景使用RingBuffer即可):
public class Main {
public static void main(String[] args) throws Exception {
//建立ringBuffer
RingBuffer<Order> ringBuffer =
RingBuffer.create(ProducerType.MULTI,
new EventFactory<Order>() {
@Override
public Order newInstance() {
return new Order();
}
},
1024 * 1024,
new YieldingWaitStrategy());
SequenceBarrier barriers = ringBuffer.newBarrier();
Consumer[] consumers = new Consumer[3];
for(int i = 0; i < consumers.length; i++){
consumers[i] = new Consumer("c" + i);
}
WorkerPool<Order> workerPool =
new WorkerPool<Order>(ringBuffer,
barriers,
new IntEventExceptionHandler(),
consumers);
ringBuffer.addGatingSequences(workerPool.getWorkerSequences());
workerPool.start(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
final CountDownLatch latch = new CountDownLatch(1);
for (int i = 0; i < 100; i++) {
final Producer p = new Producer(ringBuffer);
new Thread(new Runnable() {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int j = 0; j < 100; j ++){
p.onData(UUID.randomUUID().toString());
}
}
}).start();
}
Thread.sleep(2000);
System.out.println("---------------開始生產-----------------");
latch.countDown();
Thread.sleep(5000);
System.out.println("總數:" + consumers[0].getCount() );
}
static class IntEventExceptionHandler implements ExceptionHandler {
public void handleEventException(Throwable ex, long sequence, Object event) {}
public void handleOnStartException(Throwable ex) {}
public void handleOnShutdownException(Throwable ex) {}
}
}
public class Order {
private String id;//ID
private String name;
private double price;//金額
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
public class Producer {
private final RingBuffer<Order> ringBuffer;
public Producer(RingBuffer<Order> ringBuffer){
this.ringBuffer = ringBuffer;
}
/**
* onData用來發布事件,每呼叫一次就釋出一次事件
* 它的引數會用過事件傳遞給消費者
*/
public void onData(String data){
//可以把ringBuffer看做一個事件佇列,那麼next就是得到下面一個事件槽
long sequence = ringBuffer.next();
try {
//用上面的索引取出一個空的事件用於填充(獲取該序號對應的事件物件)
Order order = ringBuffer.get(sequence);
//獲取要通過事件傳遞的業務資料
order.setId(data);
} finally {
//釋出事件
//注意,最後的 ringBuffer.publish 方法必須包含在 finally 中以確保必須得到呼叫;如果某個請求的 sequence 未被提交,將會堵塞後續的釋出操作或者其它的 producer。
ringBuffer.publish(sequence);
}
}
}
public class Consumer implements WorkHandler<Order>{
private String consumerId;
private static AtomicInteger count = new AtomicInteger(0);
public Consumer(String consumerId){
this.consumerId = consumerId;
}
@Override
public void onEvent(Order order) throws Exception {
System.out.println("當前消費者: " + this.consumerId + ",消費資訊:" + order.getId());
count.incrementAndGet();
}
public int getCount(){
return count.get();
}
}