Disruptor從功能上來說,可以實現隊列的功能,也可以把它當成單機版的JMS來看待。從性能上來說,它比ArrayBlockingQueue有更好的性能表現,對於生產者消費者模型的業務,Disruptor是一個更好的選擇可以很好的實現業務的分離。
簡單入門
- 定義消息類,這裏的消息在Disruptor裏稱爲Event,也就是我們系統裏生產消費的業務對象,示例代碼如下:
package com.example.disruptor;
/**
* 產品
*/
public class Product {
private int id;
private String name;
private double weight;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
- 定義生產者,也就是事件的來源。
package com.example.disruptor.singleton;
import com.example.disruptor.Product;
import com.lmax.disruptor.RingBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class Producer implements Runnable {
public static final int NUMBER = 10000000;
private final CountDownLatch latch;
private AtomicInteger idCount = new AtomicInteger(0);
private RingBuffer<Product> ringBuffer;
public Producer(RingBuffer<Product> ringBuffer, CountDownLatch latch) {
this.ringBuffer = ringBuffer;
this.latch = latch;
}
private void createData() {
//1.可以把ringBuffer看做一個事件隊列,那麼next就是得到下面一個事件槽
long sequence = ringBuffer.next();
try {
//2.用上面的索引取出一個空的事件用於填充(獲取該序號對應的事件對象)
Product product = ringBuffer.get(sequence);
//3.獲取要通過事件傳遞的業務數據
product.setId(idCount.incrementAndGet());
} finally {
//4.發佈事件
//注意,最後的 ringBuffer.publish 方法必須包含在 finally 中以確保必須得到調用;
// 如果某個請求的 sequence 未被提交,將會堵塞後續的發佈操作或者其它的 producer。
ringBuffer.publish(sequence);
}
}
public void run() {
for (int i = 0; i < NUMBER; i++) {
createData();
}
//通過latch告訴主線程,完成了產品的生產
latch.countDown();
}
}
生產者在生成消息的過程中需要得到Disruptor裏的ringBuffer,將生產的消息加入到ringBuffer裏。Disruptor 的事件發佈過程是一個兩階段提交的過程:
第一步:先從 RingBuffer 獲取下一個可以寫入的事件的序號;
第二步:獲取對應的事件對象,將數據寫入事件對象;
第三部:將事件提交到 RingBuffer;
事件只有在提交之後纔會通知消息消費者進行處理;
- 定義消息的消費者,在Disruptor裏是EventHandler類型的實例。
package com.example.disruptor.singleton;
import com.example.disruptor.Product;
import com.lmax.disruptor.EventHandler;
import java.util.concurrent.CountDownLatch;
public class Consumer implements EventHandler<Product> {
private int count = 0;
private CountDownLatch latch;
public Consumer(CountDownLatch latch) {
this.latch = latch;
}
public void onEvent(Product event, long sequence, boolean endOfBatch) throws Exception {
;count++;
//通過latch告訴主線程,完成了產品的消費
if(count == Producer.NUMBER){
latch.countDown();
}
}
public int getCount() {
return count;
}
}
- 通過Disruptor類,將生產者與消費者進行整合。具體的代碼如下:
package com.example.disruptor.singleton;
import com.example.disruptor.Product;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
//定義ringBuffer的大小
private static final int RING_BUFFER_SIZE = 1024 * 8;
public static void main(String[] args) {
//構造消費者一個線程池, 實際項目中最好不要用Executors來構建
ExecutorService consumerExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
//構造生產者線程池
ExecutorService produceerExecutor = Executors.newFixedThreadPool(1);
//創建disruptor
Disruptor<Product> disruptor =
new Disruptor<Product>(new EventFactory<Product>() {
public Product newInstance() {
return new Product();
}
}, RING_BUFFER_SIZE, consumerExecutor, ProducerType.SINGLE, new YieldingWaitStrategy());
CountDownLatch latch = new CountDownLatch(2);
// 連接消費事件方法
Consumer consumer = new Consumer(latch);
disruptor.handleEventsWith(consumer);
// 啓動
disruptor.start();
//生產者開始生產數據
Producer producer = new Producer(disruptor.getRingBuffer(), latch);
produceerExecutor.submit(producer);
try {
latch.await();
} catch (InterruptedException e) {
}
System.out.println(consumer.getCount());
//關閉打開的資源
disruptor.shutdown();
consumerExecutor.shutdown();
produceerExecutor.shutdown();
}
}
在構造Disruptor對象,有幾個核心的概念:
1:事件工廠(Event Factory)定義瞭如何實例化事件(Event),Disruptor 通過 EventFactory 在 RingBuffer 中預創建 Event 的實例。
2:ringBuffer這個數組的大小,一般根據業務指定成2的指數倍。
3:消費者線程池,事件的處理是在構造的線程池裏來進行處理的。
4:指定等待策略,Disruptor 定義了 com.lmax.disruptor.WaitStrategy 接口用於抽象 Consumer 如何等待Event事件。Disruptor 提供了多個 WaitStrategy 的實現,每種策略都具有不同性能和優缺點,根據實際運行環境的 CPU 的硬件特點選擇恰當的策略,並配合特定的 JVM 的配置參數,能夠實現不同的性能提升。
BlockingWaitStrategy 是最低效的策略,但其對CPU的消耗最小並且在各種不同部署環境中能提供更加一致的性能表現;
SleepingWaitStrategy 的性能表現跟 BlockingWaitStrategy 差不多,對 CPU 的消耗也類似,但其對生產者線程的影響最小,適合用於異步日誌類似的場景;
YieldingWaitStrategy 的性能是最好的,適合用於低延遲的系統。在要求極高性能且事件處理線數小於 CPU 邏輯核心數的場景中,推薦使用此策略;例如,CPU開啓超線程的特性。
多消費者模型
在生產者消費者模型中,爲了防止生產者生產的數據覆蓋掉還未消費的數據,Disruptor中每個消費者都各自有個Sequence,而消費者的Sequence狀態需要通過SequenceBarrier同步到ringBuffer中。生產者產生數據的Sequence是通過ringBuffer進行生成的。下面是具體的代碼:
- 定義生產者
package com.example.disruptor.mult;
import com.example.disruptor.Product;
import com.lmax.disruptor.RingBuffer;
import java.util.concurrent.atomic.AtomicInteger;
public class Producer implements Runnable {
public static final int NUMBER = 10000000;
public static AtomicInteger idCount = new AtomicInteger(0);
private RingBuffer<Product> ringBuffer;
public Producer(RingBuffer<Product> ringBuffer) {
this.ringBuffer = ringBuffer;
}
private void createData() {
//1.可以把ringBuffer看做一個事件隊列,那麼next就是得到下面一個事件槽
long sequence = ringBuffer.next();
try {
//2.用上面的索引取出一個空的事件用於填充(獲取該序號對應的事件對象)
Product product = ringBuffer.get(sequence);
//3.獲取要通過事件傳遞的業務數據
product.setId(idCount.incrementAndGet());
} finally {
//4.發佈事件
//注意,最後的 ringBuffer.publish 方法必須包含在 finally 中以確保必須得到調用;
// 如果某個請求的 sequence 未被提交,將會堵塞後續的發佈操作或者其它的 producer。
ringBuffer.publish(sequence);
}
}
public void run() {
for (int i = 0; i < NUMBER; i++) {
createData();
}
}
}
- 定義消費者
package com.example.disruptor.mult;
import com.example.disruptor.Product;
import com.lmax.disruptor.WorkHandler;
/**
* 多消費者需要繼承自WorkHandler
*/
public class Consumer implements WorkHandler<Product> {
private int count = 0;
public Consumer() {
}
public void onEvent(Product event) throws Exception {
count++;
}
public int getCount() {
return count;
}
}
- 啓動類:
package com.example.disruptor.mult;
import com.example.disruptor.Product;
import com.lmax.disruptor.*;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
//定義ringBuffer的大小
private static final int RING_BUFFER_SIZE = 1024 * 8;
public static void main(String[] args) {
//線程數
int processor = Runtime.getRuntime().availableProcessors() * 2;
//構造消費者一個線程池, 實際項目中最好不要用Executors來構建
ExecutorService consumerExecutor = Executors.newFixedThreadPool(processor);
//構造生產者線程池
ExecutorService produceerExecutor = Executors.newFixedThreadPool(processor);
//定義一個ringBuffer,也就是相當於一個隊列
RingBuffer ringBuffer = RingBuffer.create(ProducerType.MULTI, new EventFactory<Product>() {
public Product newInstance() {
return new Product();
}
}, RING_BUFFER_SIZE, new YieldingWaitStrategy());
//定義一個消費者池,
Consumer[] consumers = new Consumer[processor];
for (int i = 0; i < processor; i++) {
consumers[i] = new Consumer();
}
WorkerPool workerPool = new WorkerPool<Product>(ringBuffer,
ringBuffer.newBarrier(), new IgnoreExceptionHandler(), consumers);
//每個消費者,也就是 workProcessor都有一個sequence,表示上一個消費的位置,這個在初始化時都是-1
Sequence[] sequences = workerPool.getWorkerSequences();
//將其保存在ringBuffer中的 sequencer 中,在爲生產申請slot時要用到,也就是在爲生產者申請slot時不能大於此數組中的最小值,否則產生覆蓋
ringBuffer.addGatingSequences(sequences);
//用executor 來啓動 workProcessor 線程
workerPool.start(consumerExecutor);
//生產者開始生產數據
for (int i = 0; i < processor; i++) {
Producer producer = new Producer(ringBuffer);
produceerExecutor.submit(producer);
}
while (true) {
int count = 0;
for (Consumer consumer : consumers) {
count += consumer.getCount();
}
System.out.println("生產了多少數據" + Producer.idCount.get());
System.out.println("消費了多少數據" + count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
ringBuffer需要將WorkPool裏所有消費者的Sequence加到ringBuffer中,以防止出現數據覆蓋的問題。
將disruptor當成JMS,處理消息流
可以將disruptor當成單機版的JMS,用來處理數據流,disruptor提供了消費者處理消息的先後順序,能很好的實現根據指定規則來實現消息的處理。比如可以將消息形成如下圖的數據流:- 定義上面四個handler的處理邏輯, 我這裏只貼出一個類的實現
package com.example.disruptor.complex;
import com.example.disruptor.Product;
import com.lmax.disruptor.EventHandler;
public class StartHandler implements EventHandler<Product> {
public void onEvent(Product product, long l, boolean b) throws Exception {
System.out.println("start set name");
product.setName("start");
}
}
- 定義生產都,用於生產消息
package com.example.disruptor.complex;
import com.example.disruptor.Product;
import com.lmax.disruptor.RingBuffer;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class Producer implements Runnable {
public static final int NUMBER = 2;
private AtomicInteger idCount = new AtomicInteger(0);
private RingBuffer<Product> ringBuffer;
public Producer(RingBuffer<Product> ringBuffer) {
this.ringBuffer = ringBuffer;
}
private void createData() {
//1.可以把ringBuffer看做一個事件隊列,那麼next就是得到下面一個事件槽
long sequence = ringBuffer.next();
try {
//2.用上面的索引取出一個空的事件用於填充(獲取該序號對應的事件對象)
Product product = ringBuffer.get(sequence);
//3.獲取要通過事件傳遞的業務數據
product.setId(idCount.incrementAndGet());
} finally {
//4.發佈事件
//注意,最後的 ringBuffer.publish 方法必須包含在 finally 中以確保必須得到調用;
// 如果某個請求的 sequence 未被提交,將會堵塞後續的發佈操作或者其它的 producer。
ringBuffer.publish(sequence);
}
}
public void run() {
for (int i = 0; i < NUMBER; i++) {
createData();
}
}
}
- 將disruptor與上面四個handler進行關聯
package com.example.disruptor.complex;
import com.example.disruptor.Product;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
//定義ringBuffer的大小
private static final int RING_BUFFER_SIZE = 1024 * 8;
public static void main(String[] args) {
//構造消費者一個線程池, 實際項目中最好不要用Executors來構建
ExecutorService consumerExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
//構造生產者線程池
ExecutorService produceerExecutor = Executors.newFixedThreadPool(1);
//創建disruptor
Disruptor<Product> disruptor =
new Disruptor<Product>(new EventFactory<Product>() {
public Product newInstance() {
return new Product();
}
}, RING_BUFFER_SIZE, consumerExecutor, ProducerType.SINGLE, new BlockingWaitStrategy());
//定義處理消息的handler
StartHandler start = new StartHandler();
LeftHandler left = new LeftHandler();
RightHandler right = new RightHandler();
EndHandler end = new EndHandler();
//定義處理消息的順序
disruptor.handleEventsWith(start).then(left, right).then(end);
// 啓動
disruptor.start();
// //生產者開始生產數據
Producer producer = new Producer(disruptor.getRingBuffer());
produceerExecutor.submit(producer);
//關閉打開的資源
/* disruptor.shutdown();
consumerExecutor.shutdown();
produceerExecutor.shutdown();*/
}
}
可以看到,disruptor通過提供了then方法來實現消息的先後順序語義。