Disruptor (3)- 生產與消費

生產過程

只是泛指調用 Disruptor 發佈事件的開發代碼, 與事件(Event)一樣,不是被定義的特定類型。

RingBuffer.next() -> MultiProducerSequencer.next() 得到下一個可用事件槽。如果 預分配的序列號 >(消費最慢的消費者已處理的序列號 + 容量bufferSize), 則表示當前沒有空閒的事件槽,此時將阻塞

 public long next(int n) {
        if (n < 1) {
            throw new IllegalArgumentException("n must be > 0");
        }

        long current;
        long next;

        do {
           // 當前生產的序列號
            current = cursor.get();
           // 預計下一個序列號
            next = current + n; 

            long wrapPoint = next - bufferSize; // 能夠進行生產時消費者最少要處理到的序列號
            long cachedGatingSequence = gatingSequenceCache.get(); //所有消費者組裏最小消費序列號
           // 能夠生產時最少要處理到的序列號 > 消費者內最小的消費序列號 
           // 或 消費者最小消費序列號 > 當前生產序列號 (這個條件正常情況應該不太可能,更多是考慮糾錯重新設置gatingSequenceCache)
            if (wrapPoint > cachedGatingSequence || cachedGatingSequence > current) {
                //這裏取的是: 所有消費者集合裏消費最慢的那個序號列 與 當前生產序列號 最小的值,能保證每種模式下的消費者組中的消費者都不會漏處理。
                long gatingSequence = Util.getMinimumSequence(gatingSequences, current);
                if (wrapPoint > gatingSequence) {
                //  預分配的序列號 >(消費的序列號 + 容量size), 有消費者還沒消費到wrapPoint ,當前沒有空閒事件槽需要等待固定時長
                    LockSupport.parkNanos(1); // TODO, should we spin based on the wait strategy?
                    continue;
                }
              // 更新爲:消費最少的那個消費者的已處理序列號
                gatingSequenceCache.set(gatingSequence);
            }
            // CAS方式設置next序列
            else if (cursor.compareAndSet(current, next)) {
                break;
            }
        }
        while (true);
        return next;
    }

public void publish(final long sequence)
{
    setAvailable(sequence);
    waitStrategy.signalAllWhenBlocking();//喚醒等待的消費者
}

根據源碼來分析,這個過程裏有幾個重要因素:

  1. 體現容量的概念:wrapPoint = next - bufferSize”。 表示: 若能夠在nenxt序列號上生產時消費者最少要處理到的序列。 
  2. 因爲有 串|並 的組合消費。所以這裏用來比對的是gatingSequenceCache :消費最慢的那個消費者已處理序列號;保證每種方式下的消費者都不會漏處理。

基本邏輯是:

如果: 能夠生產時最少要處理到的序列號wrapPoint > 消費者內最小(少)的消費序列號gatingSequenceCache 則自旋等待1 nanoseconds 後進入下一次計算,並更新gatingSequenceCache 爲消費最少的消費者序列號。每次分配好的next序列號會更新到生產序列器cursor中。隨後按指定生產序列號publish事件,並喚醒每一個等待中的消費線程。

消費

要素

  • com.lmax.disruptor.EventFactory<T>

生產者和消費者之間進行交換的數據被稱爲事件(Event),在Disruptor啓動初始化階段會默認創建並存儲於緩衝區數組每個下標地址中。它不是一個被 Disruptor定義的特定類型,而是由開發者指定工廠類EventFactory來創建實例;

所以 在發佈事件前,需要對RingBuffer.get(RingBuffer.next())獲取到的數據對象實例重新賦值。

  • com.lmax.disruptor.EventHandler<T> | com.lmax.disruptor.WorkHandler<T>

消費者處理類需要實現的接口,可組合使用。

獨立消費者應當實現EventHandler接口,同一事件每個消費者都會處理;Disruptor.handleEventsWith(EventHandler<? super DataEvent>... handlers)

不重複消費則實現WorkHandler接口,此時同一事件被隨機一個消費者處理;Disruptor.handleEventsWithWorkerPool(WorkHandler<? super DataEvent>... handlers)

  • com.lmax.disruptor.EventProcessor extends Runnable

消費者信息的線程Runnable對象。

在實例實現裏,一般都持有事件消費者WorkHandler | EventHandler、異常處理者ExceptionHandler、 消費者的 Sequence、序列消費控制屏障SequenceBarrier

  • com.lmax.disruptor.dsl.ConsumerRepository<T>

消費者信息倉庫,存儲了消費者線程上下文信息的封裝對象com.lmax.disruptor.dsl.ConsumerInfo。按消費模式也有兩種具體實現:

消費者線程 EventProcessor

Disruptor.start()遍歷消費者信息倉庫ConsumerRepository裏的每一個消費者ConsumerInfo執行方法ConsumerInfo.start(Executor executor);  實際上是Executor.execute執行每一個事件消費者 線程Runnable對象EventProcessor

事件處理有兩種模式:

  1. EventHandler -> BatchEventProcessor -> EventProcessorInfo
  2. WorkHandler -> WorkProcessor -> WorkerPool -> WorkerPoolInfo

分組內都共用同一個 SequenceBarrier, 每一個EventHandler都有各自獨立的 Sequence;而WorkHandlerWorkerPool層面共用一個workSequence。

EventHandler

每一個消費者都被封裝爲接口EventProcessor的實例類BatchEventProcessor,每個BatchEventProcessor有單獨的Sequence。和EventHandlerSequenceBarrier組合在一起得到接口ConsumerInfo的實例類EventProcessorInfo

關係: 1 EventHandler: 1  Sequence: 1 BatchEventProcessor : 1 EventProcessorInfo

// Disruptor
 EventHandlerGroup<T> createEventProcessors(final Sequence[] barrierSequences, final EventHandler<? super T>[] eventHandlers){
     checkNotStarted();
     // 每一個消費者都有自己的序列Sequence
     final Sequence[] processorSequences = new Sequence[eventHandlers.length];
     // 從ringBuffer中創建了序列控制閥
     final SequenceBarrier barrier = ringBuffer.newBarrier(barrierSequences);

     for (int i = 0, eventHandlersLength = eventHandlers.length; i < eventHandlersLength; i++) {
         final EventHandler<? super T> eventHandler = eventHandlers[i];
          // 每一個消費者創建線程runnable對象BatchEventProcessor
         final BatchEventProcessor<T> batchEventProcessor =
             new BatchEventProcessor<>(ringBuffer, barrier, eventHandler);

         if (exceptionHandler != null) {
             batchEventProcessor.setExceptionHandler(exceptionHandler);
         }
         // 消費者信息對象的倉庫
         consumerRepository.add(batchEventProcessor, eventHandler, barrier);
         processorSequences[i] = batchEventProcessor.getSequence();
     }
    // 給緩衝區RingBuffer增加每個消費者的序列監聽。也就是設置初始的gatingSequences
     updateGatingSequencesForNextInChain(barrierSequences, processorSequences);

     return new EventHandlerGroup<>(this, consumerRepository, processorSequences);
 }

很關鍵的一點:

創建ProcessingSequenceBarrier時傳入了參數“barrierSequences” 作爲其持有屬性dependentSequences,類型爲FixedSequenceGroup;表示需要依賴的上游消費者序列器組合WaitStrategy.waitFor方法以此來判定傳入的消費序列號是否可以被當前消費者消費。(詳見BlockingWaitStrategy,對於同享同一個ProcessingSequenceBarrier的情況用ReentrantLock來控制併發)

final class ProcessingSequenceBarrier implements SequenceBarrier{
    private final WaitStrategy waitStrategy; // 等待策略
    private final Sequence dependentSequence; // 依賴的上游消費者序列器組
    private volatile boolean alerted = false;
    private final Sequence cursorSequence; // 生產序列器
    private final Sequencer sequencer;

    ProcessingSequenceBarrier(
        final Sequencer sequencer, final WaitStrategy waitStrategy, final Sequence cursorSequence, final Sequence[] dependentSequences){
        this.sequencer = sequencer;
        this.waitStrategy = waitStrategy;
        this.cursorSequence = cursorSequence;
        if (0 == dependentSequences.length) {
            dependentSequence = cursorSequence; // 如果沒有需要依賴的上游消費者序列,則直接依賴生產序列
        } else {
            dependentSequence = new FixedSequenceGroup(dependentSequences); // 將依賴的上游消費者序列器組合, 判定時一般是取所有消費者中最少消費的那個序列號
        }
    }

// FixedSequenceGroup
public long get() {
    return Util.getMinimumSequence(sequences); // 取序列器組中值最小的
}

代碼"disruptor.handleEventsWith(new DataEventHandler("dataEventHandler1")).thenHandleEventsWithWorkerPool(new DataWorkHandler("dataWorkHandler3"));"中:

創建EventHandler消費者的ProcessingSequenceBarrier傳入“barrierSequences” 爲空數組, 所以它就直接受限於生產序列器。

WorkHandler

每一個消費者都被封裝爲接口EventProcessor的實例類WorkProcessor,多個消費者存儲在WorkerPool內的數組裏並共用一個workSequence。由WorkerPool構建消費者信息對象ConsumerInfo的實例是WorkerPoolInfo

n WorkHandler: n WorkProcessor: 1 WorkerPool: 1 WorkerPoolInfo

// Disruptor
EventHandlerGroup<T> createWorkerPool(final Sequence[] barrierSequences, final WorkHandler<? super T>[] workHandlers)
{
    final SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(barrierSequences);
    // 多個WorkHandler消費者組合在同一個WorkerPool
    final WorkerPool<T> workerPool = new WorkerPool<>(ringBuffer, sequenceBarrier, exceptionHandler, workHandlers);

    consumerRepository.add(workerPool, sequenceBarrier);

    final Sequence[] workerSequences = workerPool.getWorkerSequences();

    updateGatingSequencesForNextInChain(barrierSequences, workerSequences);

    return new EventHandlerGroup<>(this, consumerRepository, workerSequences);
}

代碼"disruptor.handleEventsWith(new DataEventHandler("dataEventHandler1")).thenHandleEventsWithWorkerPool(new DataWorkHandler("dataWorkHandler3"));"中:

創建WorkHandler消費者的ProcessingSequenceBarrier傳入“barrierSequences” 爲前一EventHandler類型消費者的序列器, 所以它就受限於上一組消費者的消費情況, 以此來達到串行先後的順序控制。

GatingSequences

上述兩種模式的消費者創建時, 都會執行方法updateGatingSequencesForNextInChain來更新RingBuffer裏成員變量AbstractSequencer實例的"gatingSequences"屬性:添入新的“processorSequences”的同時也清除掉上原有的“barrierSequences”

// Disruptor
  private void updateGatingSequencesForNextInChain(final Sequence[] barrierSequences, final Sequence[] processorSequences) {
        if (processorSequences.length > 0) {
            ringBuffer.addGatingSequences(processorSequences);
            for (final Sequence barrierSequence : barrierSequences) {
                ringBuffer.removeGatingSequence(barrierSequence);
            }
            consumerRepository.unMarkEventProcessorsAsEndOfChain(barrierSequences);
        }
    }

因爲在ProcessingSequenceBarrier的設計邏輯裏體現了消費者的依賴先後順序,所以生產階段的gatingSequences只需要關心最後一組消費者的消費情況就能判定是否可以繼續next生產。

消費者線程的執行

在本文示例代碼中, 多個EventHandler可以獨立消費同一個事件;所以它們都獨立依賴於生產序列器的生產情況的, 在線程邏輯實現裏直接用生產序列號比對。

接着對於WorkHandler是競爭消費,所以它們統一封裝成了上層結構WorkerPool,維護了統一的一個WorkSequence;當多個線程競爭執行時,CAS方式來給WorkSequence 設值,並更新自己Sequence。此時判定的因素是:依賴上游的消費序列器。

BatchEventProcessor.processEvents()爲例。

private void processEvents(){
    T event = null;
    long nextSequence = sequence.get() + 1L; // 預計消費序列 累加1

    while (true) {
        try {  //通過給控制閥定義的等待策略來判定是否阻塞下一個待分配序列; 返回的是可消費的最大有效序列。
            final long availableSequence = sequenceBarrier.waitFor(nextSequence);
            if (batchStartAware != null) {
                batchStartAware.onBatchStart(availableSequence - nextSequence + 1);
            }
           // 預計消費序列 小於等於 可消費的最大序列, 則循環消費各個中間序列的事件。
            while (nextSequence <= availableSequence) {  
                event = dataProvider.get(nextSequence);
                // 執行從RingBuffer指定序列獲取到的事件
                eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence);
                nextSequence++;
            }
            // 更新已消費序列
            sequence.set(availableSequence);
        } catch (final TimeoutException e) {
            notifyTimeout(sequence.get());
        } catch (final AlertException ex){
            if (running.get() != RUNNING){ // Disruptor.shutdown()會拋出AlertException 異常
                break;
            }
        }  catch (final Throwable ex)
        {   
            // 異常處理
            exceptionHandler.handleEventException(ex, nextSequence, event);
            sequence.set(nextSequence);
            nextSequence++;
        }
    }
}
  1. 執行 ProcessingSequenceBarrier.waitFor -> WaitStrategy.waitFor返回有效的最大可消費序列。這個過程中, 如果分配的下一個可消費序列號next比生產線程還要大則釋放鎖資源等待Condition.await();如果被依賴的上游消費者組最小消費序列號比next要小,則調用靜態方法 static void Thread.onSpinWait() 自旋等待(這需要JDK1.9的支持,否則就退化成一個while過程了)。
  2. 循環消費[next , max可消費序列號] 之間的事件,最後更新當前消費序列器的值爲: max可消費序列號。
  3. 異常通常交給ExceptionHandler.handleEventException處理。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章