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