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