disruptor異常分析

1. 背景

最近做領域驅動設計使用到disruptor組件,爲了以後不會踩坑,閱讀了disruptor部分源碼,下面對disruptor生產者和消費者異常處理進行分析。

2. Disruptor生產者異常

Disruptor生產者異常和業務耦合的地方在Translator部分,這裏從Disruptor類中的publishEvent方法入手:

// Disruptor類
public class Disruptor<T> {
  //......
  public void publishEvent(final EventTranslator<T> eventTranslator) {
    ringBuffer.publishEvent(eventTranslator);
  }
  //......
}

// RingBuffer類
public final class RingBuffer<E> extends RingBufferFields<E> implements Cursored, EventSequencer<E>, EventSink<E> {
  
  //......
  @Override
  public void publishEvent(EventTranslator<E> translator) {
    final long sequence = sequencer.next();
    translateAndPublish(translator, sequence);
  }
  
  private void translateAndPublish(EventTranslator<E> translator, long sequence) {
    try {
      // 這裏translateTo方法由業務方來重寫
      translator.translateTo(get(sequence), sequence);
    } finally {
      sequencer.publish(sequence);
    }
  }
  //......
}

RingBuffer類中的translateTo方法由業務方來重寫,若在translateTo方法發生異常,發現上面的方法並未捕捉,在translateTo拋出異常之前會執行publish方法,會直接導致放到ring buffer中的event數據不完整(不是期待中的event數據),同時該進程不能再生產消息(即若該進程依次生成了兩個event數據,若第一個event數據轉換異常會導致第二個數據不會再生成),這裏最好做一下異常捕捉。

3. 消費者異常

這裏只說明消費者實現EventHandler接口的消費者類型,該消費者類型會有一個消費者線程不斷地在輪詢處理事件,實質上是BatchEventProcessor的run方法,

public final class BatchEventProcessor<T> implements EventProcessor {
  
  @Override
  public void run() {
    
    //......
    
    try {
      while (true) {
        try {  
          final long availableSequence = sequenceBarrier.waitFor(nextSequence);
          if (batchStartAware != null) {
            batchStartAware.onBatchStart(availableSequence - nextSequence + 1);
          }
          
          // 批處理在此處得以體現
          while (nextSequence <= availableSequence) {
            event = dataProvider.get(nextSequence);
            // 消費者處理具體業務
            eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence);
            nextSequence++;
          }
          // eventHandler處理完畢後,更新當前序號
          sequence.set(availableSequence);
        } catch (final TimeoutException e) {
          notifyTimeout(sequence.get());
        } catch (final AlertException ex) {
          if (!running.get()) {
            break;
          }
        } catch (final Throwable ex) {
          // 若業務代碼中拋出異常,會被此處catch------------1
          exceptionHandler.handleEventException(ex, nextSequence, event);
          sequence.set(nextSequence);
          nextSequence++;
        }
      }
    } finally {
      //在處理器關閉之前立即通知EventHandler--------------------2
      notifyShutdown();
      running.set(false);
    }
  }
  //......
}



public final class FatalExceptionHandler implements ExceptionHandler<Object> {
    
  //......
  @Override
  public void handleEventException(final Throwable ex, final long sequence, final Object event) {
    logger.log(Level.SEVERE, "Exception processing: " + sequence + " " + event, ex);
    throw new RuntimeException(ex);
  }
  //......
}

若業務代碼中拋出異常會被上述代碼1處進行捕獲,由於FatalExceptionHandler是exceptionHandler的默認實現,所會異常會由handleEventException方法來處理,即打印出log日誌,同時拋出RuntimeException,最後會執行上面的2處,中止掉該消費者線程。會直接導致業務不被處理。

解決方案如下:

  • Step 1:實現ExceptionHandler接口

    public class DisruptorExceptionHandler<T> implements ExceptionHandler<T> {
        @Override
    	public void handleEventException(Throwable ex, long sequence, T event) {
            // 重寫該方法
        }
    }
    
  • Step 2:設置默認異常處理器(disruptor默認的是FatalExceptionHandler)

    getDisruptor().setDefaultExceptionHandler(new DisruptorExceptionHandler<T>(getThreadName()));
    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章