1. 程式人生 > >Disruptor併發框架(九)

Disruptor併發框架(九)

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();
	}

}