disruptor-3.3.2源码解析(4)-处理事件
作者:大飞
- Disruptor中如何处理事件:
disruptor中提供了专门的事件处理器接口,先看下接口定义:
/** * 事件处理器会等待RingBuffer中的事件变为可用(可处理),然后处理可用的事件。 * 一个事件处理器通常会关联一个线程。 */ public interface EventProcessor extends Runnable{ /** * 获取一个事件处理器使用的序列引用。 */ Sequence getSequence(); void halt(); boolean isRunning(); }
接下来看一个实现,BatchEventProcessor。先看下内部结构:
public final class BatchEventProcessorimplements EventProcessor{ //表示当前事件处理器的运行状态 private final AtomicBoolean running = new AtomicBoolean(false); //异常处理器。 private ExceptionHandler super T> exceptionHandler = new FatalExceptionHandler(); //数据提供者。(RingBuffer) private final DataProvider dataProvider; //序列栅栏 private final SequenceBarrier sequenceBarrier; //正真处理事件的回调接口。 private final EventHandler super T> eventHandler; //事件处理器使用的序列。 private final Sequence sequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE); //超时处理器。 private final TimeoutHandler timeoutHandler; public BatchEventProcessor(final DataProvider dataProvider, final SequenceBarrier sequenceBarrier, final EventHandler super T> eventHandler){ this.dataProvider = dataProvider; this.sequenceBarrier = sequenceBarrier; this.eventHandler = eventHandler; if (eventHandler instanceof SequenceReportingEventHandler){ ((SequenceReportingEventHandler>)eventHandler).setSequenceCallback(sequence); } timeoutHandler = (eventHandler instanceof TimeoutHandler) ? (TimeoutHandler) eventHandler : null; }
大体对内部结构有个印象,然后看一下功能方法的实现:
@Override public Sequence getSequence(){ return sequence; } @Override public void halt(){ //设置运行状态为false。 running.set(false); //通知序列栅栏。 sequenceBarrier.alert(); } @Override public boolean isRunning(){ return running.get(); }这几个方法都比较容易理解,重点看下run方法:
@Override public void run(){ //状态设置与检测。 if (!running.compareAndSet(false, true)){ throw new IllegalStateException("Thread is already running"); } //先清除序列栅栏的通知状态。 sequenceBarrier.clearAlert(); //如果eventHandler实现了LifecycleAware,这里会对其进行一个启动通知。 notifyStart(); T event = null; //获取要申请的序列值。 long nextSequence = sequence.get() + 1L; try{ while (true){ try{ //通过序列栅栏来等待可用的序列值。 final long availableSequence = sequenceBarrier.waitFor(nextSequence); //得到可用的序列值后,批量处理nextSequence到availableSequence之间的事件。 while (nextSequence <= availableSequence){ //获取事件。 event = dataProvider.get(nextSequence); //将事件交给eventHandler处理。 eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence); nextSequence++; } //处理完毕后,设置当前处理完成的最后序列值。 sequence.set(availableSequence); //继续循环。 } catch (final TimeoutException e){ //如果发生超时,通知一下超时处理器(如果eventHandler同时实现了timeoutHandler,会将其设置为当前的超时处理器) notifyTimeout(sequence.get()); }catch (final AlertException ex){ //如果捕获了序列栅栏变更通知,并且当前事件处理器停止了,那么退出主循环。 if (!running.get()){ break; } }catch (final Throwable ex){ //其他的异常都交给异常处理器进行处理。 exceptionHandler.handleEventException(ex, nextSequence, event); //处理异常后仍然会设置当前处理的最后的序列值,然后继续处理其他事件。 sequence.set(nextSequence); nextSequence++; } } } finally{ //主循环退出后,如果eventHandler实现了LifecycleAware,这里会对其进行一个停止通知。 notifyShutdown(); //设置事件处理器运行状态为停止。 running.set(false); } }
贴上其他方法:
public void setExceptionHandler(final ExceptionHandler super T> exceptionHandler){ if (null == exceptionHandler){ throw new NullPointerException(); } this.exceptionHandler = exceptionHandler; } private void notifyTimeout(final long availableSequence){ try{ if (timeoutHandler != null){ timeoutHandler.onTimeout(availableSequence); } } catch (Throwable e){ exceptionHandler.handleEventException(e, availableSequence, null); } } private void notifyStart(){ if (eventHandler instanceof LifecycleAware){ try{ ((LifecycleAware)eventHandler).onStart(); }catch (final Throwable ex){ exceptionHandler.handleOnStartException(ex); } } } private void notifyShutdown(){ if (eventHandler instanceof LifecycleAware){ try{ ((LifecycleAware)eventHandler).onShutdown(); }catch (final Throwable ex){ exceptionHandler.handleOnShutdownException(ex); } } }
总结一下BatchEventProcessor:
1. BatchEventProcessor内部会记录自己的序列、运行状态。
2. BatchEventProcessor需要外部提供数据提供者(其实就是队列-RingBuffer)、序列栅栏、异常处理器。
3. BatchEventProcessor其实是将事件委托给内部的EventHandler来处理的。
知道了这些,再结合前几篇的内容,来写个生产者/消费者模式的代码爽一下吧!
首先,要定义具体处理事件的EventHandler,先来看下这个接口:
public interface EventHandler接口很明确,没什么要解释的,然后定义我们的具体处理方式:{ /** * Called when a publisher has published an event to the {@link RingBuffer} * * @param event published to the {@link RingBuffer} * @param sequence of the event being processed * @param endOfBatch flag to indicate if this is the last event in a batch from the {@link RingBuffer} * @throws Exception if the EventHandler would like the exception handled further up the chain. */ void onEvent(T event, long sequence, boolean endOfBatch) throws Exception; }
public class MyDataEventHandler implements EventHandler{ @Override public void onEvent(MyDataEvent event, long sequence, boolean endOfBatch) throws Exception { //注意这里小睡眠了一下!! TimeUnit.SECONDS.sleep(3); System.out.println("handle event's data:" + event.getData() +"isEndOfBatch:"+endOfBatch); } }
然后是主程序:
public static void main(String[] args) { //创建一个RingBuffer,注意容量是4。 RingBufferringBuffer = RingBuffer.createSingleProducer(new MyDataEventFactory(), 4); //创建一个事件处理器。 BatchEventProcessor batchEventProcessor = /* * 注意参数:数据提供者就是RingBuffer、序列栅栏也来自RingBuffer * EventHandler使用自定义的。 */ new BatchEventProcessor (ringBuffer, ringBuffer.newBarrier(), new MyDataEventHandler()); //将事件处理器本身的序列设置为ringBuffer的追踪序列。 ringBuffer.addGatingSequences(batchEventProcessor.getSequence()); //启动事件处理器。 new Thread(batchEventProcessor).start(); //往RingBuffer上发布事件 for(int i=0;i<10;i++){ ringBuffer.publishEvent(new MyDataEventTranslatorWithIdAndValue(), i, i+"s"); System.out.println("发布事件["+i+"]"); } try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } }
注意到当前RingBuffer只有4个空间,然后发了10个事件,而且消费者内部会sleep一下,所以会不会出现事件覆盖的情况呢(生产者在RingBuffer上绕了一圈从后面追上了消费者)。
看下输出:
发布事件[0] 发布事件[1] 发布事件[2] 发布事件[3] handle event's data:MyData [id=0, value=0s]isEndOfBatch:true 发布事件[4] handle event's data:MyData [id=1, value=1s]isEndOfBatch:false handle event's data:MyData [id=2, value=2s]isEndOfBatch:false handle event's data:MyData [id=3, value=3s]isEndOfBatch:true 发布事件[5] 发布事件[6] 发布事件[7] handle event's data:MyData [id=4, value=4s]isEndOfBatch:true 发布事件[8] handle event's data:MyData [id=5, value=5s]isEndOfBatch:false handle event's data:MyData [id=6, value=6s]isEndOfBatch:false handle event's data:MyData [id=7, value=7s]isEndOfBatch:true 发布事件[9] handle event's data:MyData [id=8, value=8s]isEndOfBatch:true handle event's data:MyData [id=9, value=9s]isEndOfBatch:true发现其实并没有覆盖事件的情况,而是发布程序会等待事件处理程序处理完毕,有可发布的空间,再进行发布。还记得前面文章分析的序列的next()方法,里面会通过观察追踪序列的情况来决定是否等待,也就是说,RingBuffer需要追踪事件处理器的序列,这个关系怎么确立的呢?就是上面代码中的这句话:
//将事件处理器本身的序列设置为ringBuffer的追踪序列。 ringBuffer.addGatingSequences(batchEventProcessor.getSequence());
接下来再改一下程序,将MyDataEventHandler中的睡眠代码去掉,然后主程序中发布事件后加上睡眠代码:
public class MyDataEventHandler implements EventHandler{ @Override public void onEvent(MyDataEvent event, long sequence, boolean endOfBatch) throws Exception { //TimeUnit.SECONDS.sleep(3); System.out.println("handle event's data:" + event.getData() +"isEndOfBatch:"+endOfBatch); } } //主程序 public static void main(String[] args) { //创建一个RingBuffer,注意容量是4。 RingBuffer ringBuffer = RingBuffer.createSingleProducer(new MyDataEventFactory(), 4); //创建一个事件处理器。 BatchEventProcessor batchEventProcessor = /* * 注意参数:数据提供者就是RingBuffer、序列栅栏也来自RingBuffer * EventHandler使用自定义的。 */ new BatchEventProcessor (ringBuffer, ringBuffer.newBarrier(), new MyDataEventHandler()); //将事件处理器本身的序列设置为ringBuffer的追踪序列。 ringBuffer.addGatingSequences(batchEventProcessor.getSequence()); //启动事件处理器。 new Thread(batchEventProcessor).start(); //往RingBuffer上发布事件 for(int i=0;i<10;i++){ ringBuffer.publishEvent(new MyDataEventTranslatorWithIdAndValue(), i, i+"s"); System.out.println("发布事件["+i+"]"); try { TimeUnit.SECONDS.sleep(3);//睡眠!!! } catch (InterruptedException e) { e.printStackTrace(); } } try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } }
这种情况下,事件处理很快,但是发布事件很慢,事件处理器会等待事件的发布么?
看下输出:
发布事件[0] handle event's data:MyData [id=0, value=0s]isEndOfBatch:true 发布事件[1] handle event's data:MyData [id=1, value=1s]isEndOfBatch:true 发布事件[2] handle event's data:MyData [id=2, value=2s]isEndOfBatch:true 发布事件[3] handle event's data:MyData [id=3, value=3s]isEndOfBatch:true 发布事件[4] handle event's data:MyData [id=4, value=4s]isEndOfBatch:true 发布事件[5] handle event's data:MyData [id=5, value=5s]isEndOfBatch:true 发布事件[6] handle event's data:MyData [id=6, value=6s]isEndOfBatch:true 发布事件[7] handle event's data:MyData [id=7, value=7s]isEndOfBatch:true 发布事件[8] handle event's data:MyData [id=8, value=8s]isEndOfBatch:true 发布事件[9] handle event's data:MyData [id=9, value=9s]isEndOfBatch:true可见是会等待的,关键就是,事件处理器用的是RingBuffer的序列栅栏,会在栅栏上waitFor事件发布者:
new BatchEventProcessor(ringBuffer,/*注意这里*/ringBuffer.newBarrier(), new MyDataEventHandler());
接下来,我们在改变一下姿势,再加一个事件处理器,看看会发生什么:
public class KickAssEventHandler implements EventHandler{ @Override public void onEvent(MyDataEvent event, long sequence, boolean endOfBatch) throws Exception { System.out.println("kick your ass "+sequence+" times!!!!"); } }
主程序如下:
public static void main(String[] args) { //创建一个RingBuffer,注意容量是4。 RingBufferringBuffer = RingBuffer.createSingleProducer(new MyDataEventFactory(), 4); //创建一个事件处理器。 BatchEventProcessor batchEventProcessor = /* * 注意参数:数据提供者就是RingBuffer、序列栅栏也来自RingBuffer * EventHandler使用自定义的。 */ new BatchEventProcessor (ringBuffer, ringBuffer.newBarrier(), new MyDataEventHandler()); //创建一个事件处理器。 BatchEventProcessor batchEventProcessor2 = /* * 注意参数:数据提供者就是RingBuffer、序列栅栏也来自RingBuffer * EventHandler使用自定义的。 */ new BatchEventProcessor (ringBuffer, ringBuffer.newBarrier(), new KickAssEventHandler()); //将事件处理器本身的序列设置为ringBuffer的追踪序列。 ringBuffer.addGatingSequences(batchEventProcessor.getSequence()); ringBuffer.addGatingSequences(batchEventProcessor2.getSequence()); //启动事件处理器。 new Thread(batchEventProcessor).start(); new Thread(batchEventProcessor2).start(); //往RingBuffer上发布事件 for(int i=0;i<10;i++){ ringBuffer.publishEvent(new MyDataEventTranslatorWithIdAndValue(), i, i+"s"); System.out.println("发布事件["+i+"]"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } }
看下输出:
发布事件[0] kick your ass 0 times!!!! handle event's data:MyData [id=0, value=0s]isEndOfBatch:true 发布事件[1] kick your ass 1 times!!!! handle event's data:MyData [id=1, value=1s]isEndOfBatch:true 发布事件[2] handle event's data:MyData [id=2, value=2s]isEndOfBatch:true kick your ass 2 times!!!! 发布事件[3] handle event's data:MyData [id=3, value=3s]isEndOfBatch:true kick your ass 3 times!!!! 发布事件[4] kick your ass 4 times!!!! handle event's data:MyData [id=4, value=4s]isEndOfBatch:true 发布事件[5] kick your ass 5 times!!!! handle event's data:MyData [id=5, value=5s]isEndOfBatch:true 发布事件[6] handle event's data:MyData [id=6, value=6s]isEndOfBatch:true kick your ass 6 times!!!! 发布事件[7] kick your ass 7 times!!!! handle event's data:MyData [id=7, value=7s]isEndOfBatch:true 发布事件[8] kick your ass 8 times!!!! handle event's data:MyData [id=8, value=8s]isEndOfBatch:true 发布事件[9] kick your ass 9 times!!!! handle event's data:MyData [id=9, value=9s]isEndOfBatch:true
相当于有两个消费者,消费相同的数据,又有点像发布/订阅模式了,呵呵。
当然,按照上面源码的分析,我们还可以让我们的Eventhandler同时实现TimeoutHandler和LifecycleAware来做更多的事;还可以定制一个ExceptionHandler来处理异常情况(框架中也提供了IgnoreExceptionHandler和FatalExceptionHandler两种ExceptionHandler实现,它们在异常处理时会记录不用级别的日志,后者还会抛出运行时异常)。
Disruptor还提供了一个队列接口-EventPoller。通过这个接口也可以用RingBuffer中获取事件并处理,而且这个获取方式是无等待的,如果当前没有可处理的事件,会返回相应的状态-PollState。但注释说明这还是个实验性质的类,这里就不分析了。
- Work消费模式:
看完上面的内容,可能会有这样的疑惑:实际用的时候可能不会只用单线程来消费吧,能不能使用多个线程来消费同一批事件,每个线程消费这批事件中的一部分呢?
disruptor对这种情况也进行了支持,如果上面的叫Event模式,那么这个就叫Work模式吧(不要在意这些名词,知道是怎么回事儿就行了...)
public final class WorkProcessorimplements EventProcessor{ private final AtomicBoolean running = new AtomicBoolean(false); private final Sequence sequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE); private final RingBuffer ringBuffer; private final SequenceBarrier sequenceBarrier; private final WorkHandler super T> workHandler; private final ExceptionHandler super T> exceptionHandler; private final Sequence workSequence; private final EventReleaser eventReleaser = new EventReleaser(){ @Override public void release(){ sequence.set(Long.MAX_VALUE); } }; public WorkProcessor(final RingBuffer ringBuffer, final SequenceBarrier sequenceBarrier, final WorkHandler super T> workHandler, final ExceptionHandler super T> exceptionHandler, final Sequence workSequence){ this.ringBuffer = ringBuffer; this.sequenceBarrier = sequenceBarrier; this.workHandler = workHandler; this.exceptionHandler = exceptionHandler; this.workSequence = workSequence; if (this.workHandler instanceof EventReleaseAware){ ((EventReleaseAware)this.workHandler).setEventReleaser(eventReleaser); } }
看起来结构和BatchEventProcessor类似,区别是EventHandler变成了WorkHandler,还有了额外的workSequence和一个eventReleaser。
@Override public Sequence getSequence(){ return sequence; } @Override public void halt(){ running.set(false); sequenceBarrier.alert(); } @Override public boolean isRunning(){ return running.get(); }这些方法和BatchEventProcessor的一样,不说了。看下主逻辑:
@Override public void run(){ //状态设置与检测。 if (!running.compareAndSet(false, true)){ throw new IllegalStateException("Thread is already running"); } //先清除序列栅栏的通知状态。 sequenceBarrier.clearAlert(); //如果workHandler实现了LifecycleAware,这里会对其进行一个启动通知。 notifyStart(); boolean processedSequence = true; long cachedAvailableSequence = Long.MIN_VALUE; long nextSequence = sequence.get(); T event = null; while (true){ try{ //判断上一个事件是否已经处理完毕。 if (processedSequence){ //如果处理完毕,重置标识。 processedSequence = false; //原子的获取下一要处理事件的序列值。 do{ nextSequence = workSequence.get() + 1L; sequence.set(nextSequence - 1L); }while (!workSequence.compareAndSet(nextSequence - 1L, nextSequence)); } //检查序列值是否需要申请。这一步是为了防止和事件生产者冲突。 if (cachedAvailableSequence >= nextSequence){ //从RingBuffer上获取事件。 event = ringBuffer.get(nextSequence); //委托给workHandler处理事件。 workHandler.onEvent(event); //设置事件处理完成标识。 processedSequence = true; }else{ //如果需要申请,通过序列栅栏来申请可用的序列。 cachedAvailableSequence = sequenceBarrier.waitFor(nextSequence); } }catch (final AlertException ex){ //处理通知。 if (!running.get()){ //如果当前处理器被停止,那么退出主循环。 break; } }catch (final Throwable ex){ //处理异常 exceptionHandler.handleEventException(ex, nextSequence, event); //如果异常处理器不抛出异常的话,就认为事件处理完毕,设置事件处理完成标识。 processedSequence = true; } } //退出主循环后,如果workHandler实现了LifecycleAware,这里会对其进行一个关闭通知。 notifyShutdown(); //设置当前处理器状态为停止。 running.set(false); }
说明一下WorkProcessor的主逻辑中几个重点:
1.首先,由于是Work模式,必然是多个事件处理者(WorkProcessor)处理同一批事件,那么肯定会存在多个处理者对同一个要处理事件的竞争,所以出现了一个workSequence,所有的处理者都使用这一个workSequence,大家通过对workSequence的原子操作来保证不会处理相同的事件。
2.其次,多个事件处理者和事件发布者之间也需要协调,需要等待事件发布者发布完事件之后才能对其进行处理,这里还是使用序列栅栏来协调(sequenceBarrier.waitFor)。
public final class WorkerPool{ //运行状态标识。 private final AtomicBoolean started = new AtomicBoolean(false); //工作序列。 private final Sequence workSequence = new Sequence(Sequencer.INITIAL_CURSOR_VALUE); //事件队列。 private final RingBuffer ringBuffer; //事件处理器数组。 private final WorkProcessor>[] workProcessors; public WorkerPool(final RingBuffer ringBuffer, final SequenceBarrier sequenceBarrier, final ExceptionHandler super T> exceptionHandler, final WorkHandler super T>... workHandlers){ this.ringBuffer = ringBuffer; final int numWorkers = workHandlers.length; workProcessors = new WorkProcessor[numWorkers]; for (int i = 0; i < numWorkers; i++){ workProcessors[i] = new WorkProcessor (ringBuffer, sequenceBarrier, workHandlers[i], exceptionHandler, workSequence); } } public WorkerPool(final EventFactory eventFactory, final ExceptionHandler super T> exceptionHandler, final WorkHandler super T>... workHandlers){ ringBuffer = RingBuffer.createMultiProducer(eventFactory, 1024, new BlockingWaitStrategy()); final SequenceBarrier barrier = ringBuffer.newBarrier(); final int numWorkers = workHandlers.length; workProcessors = new WorkProcessor[numWorkers]; for (int i = 0; i < numWorkers; i++){ workProcessors[i] = new WorkProcessor (ringBuffer, barrier, workHandlers[i], exceptionHandler, workSequence); } ringBuffer.addGatingSequences(getWorkerSequences()); }
WorkerPool是Work模式下的事件处理器池,它起到了维护事件处理器生命周期、关联事件处理器与事件队列(RingBuffer)、提供工作序列(上面分析WorkProcessor时看到多个处理器需要使用统一的workSequence)的作用。
public Sequence[] getWorkerSequences(){ final Sequence[] sequences = new Sequence[workProcessors.length + 1]; for (int i = 0, size = workProcessors.length; i < size; i++){ sequences[i] = workProcessors[i].getSequence(); } sequences[sequences.length - 1] = workSequence; return sequences; }通过WorkerPool是可以获取内部事件处理器各自的序列和当前的WorkSequence,用于观察事件处理进度。
public RingBufferstart方法里面会初始化工作序列,然后使用一个给定的执行器(线程池)来执行内部的事件处理器。start(final Executor executor){ if (!started.compareAndSet(false, true)){ throw new IllegalStateException("WorkerPool has already been started and cannot be restarted until halted."); } final long cursor = ringBuffer.getCursor(); workSequence.set(cursor); for (WorkProcessor> processor : workProcessors){ processor.getSequence().set(cursor); executor.execute(processor); } return ringBuffer; }
public void drainAndHalt(){ Sequence[] workerSequences = getWorkerSequences(); while (ringBuffer.getCursor() > Util.getMinimumSequence(workerSequences)){ Thread.yield(); } for (WorkProcessor> processor : workProcessors){ processor.halt(); } started.set(false); }drainAndHalt方法会将RingBuffer中所有的事件取出,执行完毕后,然后停止当前WorkerPool。
public void halt(){ for (WorkProcessor> processor : workProcessors){ processor.halt(); } started.set(false); }马上停止当前WorkerPool。
public boolean isRunning(){ return started.get(); }
获取当前WorkerPool运行状态。
好了,有了WorkProcessor和WorkerPool,我们又可以嗨皮的写一下多线程消费的生产者/消费者模式了!
还记得WorkProcessor由内部的WorkHandler来具体处理事件吧,所以先写一个WorkHandler:
public class MyDataWorkHandler implements WorkHandler{ private String name; public MyDataWorkHandler(String name) { this.name = name; } @Override public void onEvent(MyDataEvent event) throws Exception { System.out.println("WorkHandler["+name+"]处理事件"+event.getData()); } }
然后是主程序:(其他的类源码见上篇)
public static void main(String[] args) { //创建一个RingBuffer,注意容量是4。 RingBuffer输出如下:ringBuffer = RingBuffer.createSingleProducer(new MyDataEventFactory(), 4); //创建3个WorkHandler MyDataWorkHandler handler1 = new MyDataWorkHandler("1"); MyDataWorkHandler handler2 = new MyDataWorkHandler("2"); MyDataWorkHandler handler3 = new MyDataWorkHandler("3"); WorkerPool workerPool = new WorkerPool (ringBuffer, ringBuffer.newBarrier(), new IgnoreExceptionHandler(), handler1,handler2,handler3); //将WorkPool的工作序列集设置为ringBuffer的追踪序列。 ringBuffer.addGatingSequences(workerPool.getWorkerSequences()); //创建一个线程池用于执行Workhandler。 Executor executor = Executors.newFixedThreadPool(4); //启动WorkPool。 workerPool.start(executor); //往RingBuffer上发布事件 for(int i=0;i<10;i++){ ringBuffer.publishEvent(new MyDataEventTranslatorWithIdAndValue(), i, i+"s"); System.out.println("发布事件["+i+"]"); } try { System.in.read(); } catch (IOException e) { e.printStackTrace(); } }
发布事件[0] WorkHandler[1]处理事件MyData [id=0, value=0s] 发布事件[1] 发布事件[2] WorkHandler[1]处理事件MyData [id=3, value=3s] 发布事件[3] WorkHandler[2]处理事件MyData [id=2, value=2s] WorkHandler[3]处理事件MyData [id=1, value=1s] 发布事件[4] WorkHandler[1]处理事件MyData [id=4, value=4s] 发布事件[5] WorkHandler[2]处理事件MyData [id=5, value=5s] WorkHandler[3]处理事件MyData [id=6, value=6s] 发布事件[6] 发布事件[7] WorkHandler[1]处理事件MyData [id=7, value=7s] 发布事件[8] 发布事件[9] WorkHandler[2]处理事件MyData [id=8, value=8s] WorkHandler[3]处理事件MyData [id=9, value=9s]
正是我们想要的效果,多个处理器处理同一批事件!
- 事件处理者如何等待事件发布者发布事件:
上面的分析中已经提到了事件发布者会使用序列栅栏来等待(waitFor)事件发布者发布事件,但具体是怎么等待呢?是阻塞还是自旋?接下来仔细看下这部分,Disruptor框架对这部分提供了很多策略。
public 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); } } @Override public long waitFor(final long sequence) throws AlertException, InterruptedException, TimeoutException{ checkAlert(); long availableSequence = waitStrategy.waitFor(sequence, cursorSequence, dependentSequence, this); if (availableSequence < sequence){ return availableSequence; } return sequencer.getHighestPublishedSequence(sequence, availableSequence); }我们注意到waitFor方法中其实是调用了WaitStrategy的waitFor方法来实现等待,WaitStrategy接口是框架中定义的等待策略接口(其实在RingBuffer构造的可以传入一个等待策略,没显式传入的话,会使用默认的等待策略,可以查看下RingBuffer的构造方法),先看下这个接口:
public interface WaitStrategy{ /** * 等待给定的sequence变为可用。 * * @param sequence 等待(申请)的序列值。 * @param cursor ringBuffer中的主序列,也可以认为是事件发布者使用的序列。 * @param dependentSequence 事件处理者使用的序列。 * @param barrier 序列栅栏。 * @return 对事件处理者来说可用的序列值,可能会比申请的序列值大。 * @throws AlertException if the status of the Disruptor has changed. * @throws InterruptedException if the thread is interrupted. * @throws TimeoutException */ long waitFor(long sequence, Sequence cursor, Sequence dependentSequence, SequenceBarrier barrier) throws AlertException, InterruptedException, TimeoutException; /** * 当发布事件成功后会调用这个方法来通知等待的事件处理者序列可用了。 */ void signalAllWhenBlocking(); }
Disruptor框架中提供了如下几种等待策略:
BlockingWaitStrategy
BusySpinWaitStrategy
LiteBlockingWaitStrategy
SleepingWaitStrategy
TimeoutBlockingWaitStrategy
YieldingWaitStrategy
PhasedBackoffWaitStrategy
public final class BlockingWaitStrategy implements WaitStrategy{ private final Lock lock = new ReentrantLock(); private final Condition processorNotifyCondition = lock.newCondition(); @Override public long waitFor(long sequence, Sequence cursorSequence, Sequence dependentSequence, SequenceBarrier barrier) throws AlertException, InterruptedException{ long availableSequence; if ((availableSequence = cursorSequence.get()) < sequence){ //如果RingBuffer上当前可用的序列值小于要申请的序列值。 lock.lock(); try{ //再次检测 while ((availableSequence = cursorSequence.get()) < sequence){ //检查序列栅栏状态(事件处理器是否被关闭) barrier.checkAlert(); //当前线程在processorNotifyCondition条件上等待。 processorNotifyCondition.await(); } }finally{ lock.unlock(); } } //再次检测,避免事件处理器关闭的情况。 while ((availableSequence = dependentSequence.get()) < sequence){ barrier.checkAlert(); } return availableSequence; } @Override public void signalAllWhenBlocking(){ lock.lock(); try{ //唤醒在processorNotifyCondition条件上等待的处理事件线程。 processorNotifyCondition.signalAll(); }finally{ lock.unlock(); } } }
可见BlockingWaitStrategy的实现方法是阻塞等待。当要求节省CPU资源,而不要求高吞吐量和低延迟的时候使用这个策略。
public final class BusySpinWaitStrategy implements WaitStrategy{ @Override public long waitFor(final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)throws AlertException, InterruptedException{ long availableSequence; while ((availableSequence = dependentSequence.get()) < sequence){ barrier.checkAlert(); //自旋 } return availableSequence; } @Override public void signalAllWhenBlocking(){ } }
BusySpinWaitStrategy的实现方法是自旋等待。这种策略会利用CPU资源来避免系统调用带来的延迟抖动,当线程可以绑定到指定CPU(核)的时候可以使用这个策略。
public final class LiteBlockingWaitStrategy implements WaitStrategy{ private final Lock lock = new ReentrantLock(); private final Condition processorNotifyCondition = lock.newCondition(); private final AtomicBoolean signalNeeded = new AtomicBoolean(false); @Override public long waitFor(long sequence, Sequence cursorSequence, Sequence dependentSequence, SequenceBarrier barrier) throws AlertException, InterruptedException{ long availableSequence; if ((availableSequence = cursorSequence.get()) < sequence) { lock.lock(); try{ do{ signalNeeded.getAndSet(true); if ((availableSequence = cursorSequence.get()) >= sequence) { break; } barrier.checkAlert(); processorNotifyCondition.await(); } while ((availableSequence = cursorSequence.get()) < sequence); }finally{ lock.unlock(); } } while ((availableSequence = dependentSequence.get()) < sequence){ barrier.checkAlert(); } return availableSequence; } @Override public void signalAllWhenBlocking(){ if (signalNeeded.getAndSet(false)){ lock.lock(); try{ processorNotifyCondition.signalAll(); }finally{ lock.unlock(); } } } }
相比BlockingWaitStrategy,LiteBlockingWaitStrategy的实现方法也是阻塞等待,但它会减少一些不必要的唤醒。从源码的注释上看,这个策略在基准性能测试上是会表现出一些性能提升,但是作者还不能完全证明程序的正确性。
public final class SleepingWaitStrategy implements WaitStrategy{ private static final int DEFAULT_RETRIES = 200; private final int retries; public SleepingWaitStrategy(){ this(DEFAULT_RETRIES); } public SleepingWaitStrategy(int retries){ this.retries = retries; } @Override public long waitFor(final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier) throws AlertException, InterruptedException{ long availableSequence; int counter = retries; while ((availableSequence = dependentSequence.get()) < sequence){ counter = applyWaitMethod(barrier, counter); } return availableSequence; } @Override public void signalAllWhenBlocking(){ } private int applyWaitMethod(final SequenceBarrier barrier, int counter) throws AlertException{ barrier.checkAlert(); //从指定的重试次数(默认是200)重试到剩下100次,这个过程是自旋。 if (counter > 100){ --counter; } //然后尝试100次让出处理器动作。 else if (counter > 0){ --counter; Thread.yield(); } //然后尝试阻塞1纳秒。 else{ LockSupport.parkNanos(1L); } return counter; } }
SleepingWaitStrategy的实现方法是先自旋,不行再临时让出调度(yield),不行再短暂的阻塞等待。对于既想取得高性能,由不想太浪费CPU资源的场景,这个策略是一种比较好的折中方案。使用这个方案可能会出现延迟波动。
public class TimeoutBlockingWaitStrategy implements WaitStrategy{ private final Lock lock = new ReentrantLock(); private final Condition processorNotifyCondition = lock.newCondition(); private final long timeoutInNanos; public TimeoutBlockingWaitStrategy(final long timeout, final TimeUnit units){ timeoutInNanos = units.toNanos(timeout); } @Override public long waitFor(final long sequence, final Sequence cursorSequence, final Sequence dependentSequence, final SequenceBarrier barrier) throws AlertException, InterruptedException, TimeoutException{ long nanos = timeoutInNanos; long availableSequence; if ((availableSequence = cursorSequence.get()) < sequence){ lock.lock(); try{ while ((availableSequence = cursorSequence.get()) < sequence){ barrier.checkAlert(); nanos = processorNotifyCondition.awaitNanos(nanos); if (nanos <= 0){ throw TimeoutException.INSTANCE; } } }finally{ lock.unlock(); } } while ((availableSequence = dependentSequence.get()) < sequence){ barrier.checkAlert(); } return availableSequence; } @Override public void signalAllWhenBlocking(){ lock.lock(); try{ processorNotifyCondition.signalAll(); }finally{ lock.unlock(); } } }
TimeoutBlockingWaitStrategy的实现方法是阻塞给定的时间,超过时间的话会抛出超时异常。
public final class YieldingWaitStrategy implements WaitStrategy{ private static final int SPIN_TRIES = 100; @Override public long waitFor(final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier) throws AlertException, InterruptedException{ long availableSequence; int counter = SPIN_TRIES; while ((availableSequence = dependentSequence.get()) < sequence){ counter = applyWaitMethod(barrier, counter); } return availableSequence; } @Override public void signalAllWhenBlocking(){ } private int applyWaitMethod(final SequenceBarrier barrier, int counter) throws AlertException{ barrier.checkAlert(); if (0 == counter){ Thread.yield(); }else{ --counter; } return counter; } }
SleepingWaitStrategy的实现方法是先自旋(100次),不行再临时让出调度(yield)。和SleepingWaitStrategy一样也是一种高性能与CPU资源之间取舍的折中方案,但这个策略不会带来显著的延迟抖动。
public final class PhasedBackoffWaitStrategy implements WaitStrategy{ private static final int SPIN_TRIES = 10000; private final long spinTimeoutNanos; private final long yieldTimeoutNanos; private final WaitStrategy fallbackStrategy; public PhasedBackoffWaitStrategy(long spinTimeout, long yieldTimeout, TimeUnit units, WaitStrategy fallbackStrategy){ this.spinTimeoutNanos = units.toNanos(spinTimeout); this.yieldTimeoutNanos = spinTimeoutNanos + units.toNanos(yieldTimeout); this.fallbackStrategy = fallbackStrategy; } public static PhasedBackoffWaitStrategy withLock(long spinTimeout, long yieldTimeout, TimeUnit units){ return new PhasedBackoffWaitStrategy(spinTimeout, yieldTimeout, units, new BlockingWaitStrategy()); } public static PhasedBackoffWaitStrategy withLiteLock(long spinTimeout, long yieldTimeout, TimeUnit units){ return new PhasedBackoffWaitStrategy(spinTimeout, yieldTimeout, units, new LiteBlockingWaitStrategy()); } public static PhasedBackoffWaitStrategy withSleep(long spinTimeout, long yieldTimeout, TimeUnit units){ return new PhasedBackoffWaitStrategy(spinTimeout, yieldTimeout, units, new SleepingWaitStrategy(0)); } @Override public long waitFor(long sequence, Sequence cursor, Sequence dependentSequence, SequenceBarrier barrier) throws AlertException, InterruptedException, TimeoutException{ long availableSequence; long startTime = 0; int counter = SPIN_TRIES; do{ if ((availableSequence = dependentSequence.get()) >= sequence){ return availableSequence; } if (0 == --counter){ if (0 == startTime){ startTime = System.nanoTime(); }else{ long timeDelta = System.nanoTime() - startTime; if (timeDelta > yieldTimeoutNanos){ return fallbackStrategy.waitFor(sequence, cursor, dependentSequence, barrier); }else if (timeDelta > spinTimeoutNanos){ Thread.yield(); } } counter = SPIN_TRIES; } }while (true); } @Override public void signalAllWhenBlocking(){ fallbackStrategy.signalAllWhenBlocking(); } }
PhasedBackoffWaitStrategy的实现方法是先自旋(10000次),不行再临时让出调度(yield),不行再使用其他的策略进行等待。可以根据具体场景自行设置自旋时间、yield时间和备用等待策略。
- 最后总结:
1.事件处理者可以通过Event模式或者Work模式来处理事件。