Disruptor源码解析四 消费者的组织串联

目录

 

系列索引

前言

消费者架构

消费者接口

消费者的串行与并行

并行

串行


系列索引

前言

  • 之前介绍了RingBuffer和内部Sequencer的具体实现,disruptor的生产者操作直接用ringBuffer的相关接口调用即可。
  • 本节主要解释一下,消费者的主要串联方式。

消费者架构

消费者的组织

一个生产者P1与三个消费者C1、C2、C3,C3的事件处理需要C1与C2先完成。则该模型结构如下:

每个消费者都是异步的,便于并行处理业务(如http或者rpc请求)。

消费者接口

Disruptor类中,添加消费者有4个接口,实际是3种实现方式,分别是EventHandler, EventProcessor,WorkHandler。

public class Disruptor<T>{
    // 添加并行消费者,每一个EventHandler都是独立的消费者。  这些消费者是并行关系,彼此无依赖的。
    public final EventHandlerGroup<T> handleEventsWith(final EventHandler<? super T>... handlers){}

    // 添加并行消费者,每一个EventProcessorFactory创建一个EventProcessor映射为一个消费者。  这些消费者之间是并行关系
    public final EventHandlerGroup<T> handleEventsWith(final EventProcessorFactory<T>... eventProcessorFactories){}

    // 添加并行消费者,每一个EventProcessor映射为一个消费者。 这些消费者之间是并行关系
    public EventHandlerGroup<T> handleEventsWith(final EventProcessor... processors){}

    // 添加一个多线程的消费者。该消费者会将事件分发到各个WorkHandler。每一个事件只会被其中一个WorkHandler处理。
    public final EventHandlerGroup<T> handleEventsWithWorkerPool(final WorkHandler<T>... workHandlers){}
}



public interface EventHandler<T>{
  /**
   *  {@link BatchEventProcessor}会批量的从RingBuffer中取出数据,然后逐个调用该方法进行处理
   *
   * 警告:如果在处理事件时抛出异常,而没有指定{@link ExceptionHandler}时,会导致BatchEventProcessor停止工作,可能导致死锁!
   *  -> 系统默认的异常处理{@link FatalExceptionHandler}会将异常包装为RuntimeException重新抛出,直接退出循环吗,会导致死锁。
   *
   * 这样的好处是,你可以降低一些操作的消耗,可以攒到批量数据的结尾时进行一次操作。
   * 如IO操作,对写复制容器的操作(写入时尽量将多次写入合并为一次写入)。
   * @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;
}


/**
 * 事件处理器(EventHandler的代理对象)
 * 一个事件处理器实现Runnable接口,在轮询时使用适当的等待策略(WaitStrategy)从RingBuffer中轮询获取可用事件(拉取数据)。
 * 你最好不实现该接口而是优先使用EventHandler接口和BatchEventProcessor。
 *
 * 划重点:
 * 1.事件处理器是最小的事件处理单元,继承Runnable接口,是一个无限执行的任务(轮询监听),在运行时不会让出线程,因此每一个事件处理器需要独立的线程。
 * 2.一个消费者中可能只有一个事件处理器(BatchEventProcessor),也可能有多个事件处理器(WorkPool)。
 * 3.BatchEventProcessor既是事件处理器,也是消费者。
 *   WorkProcessor是事件处理器,但不是消费者,WorkPool才是消费者。
 * 4.管理消费者,其实也是管理消费们的所有事件处理器。
 * 5.每一个事件处理器都有自己独立的Sequence(进度),如果是多个事件处理器协作的话,这些处理器之间会进行同步。
 */
public interface EventProcessor extends Runnable
{
    /**
	 * 获取事情处理器的Sequence(进度),每一个事件处理器有自己独立的Sequence,
     * @return reference to the {@link Sequence} for this {@link EventProcessor}
     */
    Sequence getSequence();

    /**
	 * 通知事件处理器在完成本次消费之后,暂停下来。协作指令(类似中断)
     */
    void halt();

	/**
	 * 查询事件处理器是否运行中
	 */
    boolean isRunning();
}


/**
 * 事件处理器工厂
 */
public interface EventProcessorFactory<T>
{
    EventProcessor createEventProcessor(RingBuffer<T> ringBuffer, Sequence[] barrierSequences);
}


/**
 * 它是{@link WorkProcessor}处理事件的实现,两者之间是组合关系,{@link WorkProcessor}负责跟踪和拉取事件,{@link WorkHandler}负责处理事件。
 * 手动加粗:{@link WorkHandler}不是消费者。
 */
public interface WorkHandler<T>{
    void onEvent(T event) throws Exception;
}

可以看到,有3中消费者接口 

  • EventHandler 就是一个最简单的接口,被EventProcessor代理。
  • EventProcessor是一个具体的消费者,可中断,实现类有
    • BatchEventProcessor:  单线程批处理消费者,一次添加多个时每个event会被所有的消费者消费。
    • WorkProcessor: WorkPool消费者里面的事件处理单元,不是消费者,只是一个消费者里面的一个工作单元, 多个WorkProcessor协作构成WorkPool消费者。一次添加多个时,每个event只会被一个process消费。
  • WorkHandler: WorkPool 里的处理接口,WorkProcessor负责跟踪和拉取事件,WorkHandler负责处理事件。

具体下来其实有两种消费者工作模式:  单线程批处理模式(类比多个consumerGroup) 以及 workPool模式(类比一个consumerGroup)。消费者的具体实现放在下一章节分享,本节主要分享如何链式组织消费者,这一块的实现主要依赖EventHandlerGroup。

消费者的串行与并行

并行

消费者的并行实现比较简单,通过handleEventsWith 参数为EventHandler,EventProcessor 或者EventProcessorFactory的方法都是添加的多个单线程批处理消费者,这些消费者是并行的,即每一个event走到这一层(其实是指EventHandlerGroup)都会被所有的消费者消费一遍。

也可以通过EventHandlerGroup的add方法实现并行。

串行

Disrutor的handleEventsWith返回的对象都是EventHandlerGroup,其实就是把当前传入的所有消费者,当成了串行流程的一个节点,这个节点的名称叫做EventHandlerGroup。

之前的章节还有分享过SequenceBarrier的概念,每个串行节点前必须有一个SequenceBarrier,来控制能不能消费,第一个EventHandlerGroup节点的SequenceBarrier是生产者的游标,之后每一个串行的EventHandlerGroup依赖的SequenceBarrier都是前一个节点的最小消费Sequence。

Disruptor代码分享如下(以添加eventHandler为例):

public class Disruptor<T>{

    public final EventHandlerGroup<T> handleEventsWith(final EventHandler<? super T>... handlers)
    {
        return createEventProcessors(new Sequence[0], handlers);
    }

  /**
   * 创建事件处理器(创建BatchEventProcessor消费者)
   * @param barrierSequences 屏障sequences,消费者的消费进度需要慢于它的前驱消费者
   * @param eventHandlers 事件处理方法 每一个EventHandler都会被包装为{@link BatchEventProcessor}(一个独立的消费者).
   * @return
   */
    EventHandlerGroup<T> createEventProcessors(
        final Sequence[] barrierSequences,
        final EventHandler<? super T>[] eventHandlers)
    {
      // 组织消费者之间的关系只能在启动之前
        checkNotStarted();

        // 收集添加的消费者的序号
        final Sequence[] processorSequences = new Sequence[eventHandlers.length];
        // 本批次消费由于添加在同一个节点之后,因此共享该屏障
        final SequenceBarrier barrier = ringBuffer.newBarrier(barrierSequences);

        // 创建单线程消费者(BatchEventProcessor)
        for (int i = 0, eventHandlersLength = eventHandlers.length; i < eventHandlersLength; i++)
        {
            final EventHandler<? super T> eventHandler = eventHandlers[i];

            final BatchEventProcessor<T> batchEventProcessor =
                new BatchEventProcessor<>(ringBuffer, barrier, eventHandler);

            if (exceptionHandler != null)
            {
                batchEventProcessor.setExceptionHandler(exceptionHandler);
            }

            // 添加到消费者信息仓库中
            consumerRepository.add(batchEventProcessor, eventHandler, barrier);
            processorSequences[i] = batchEventProcessor.getSequence();
        }

        // 更新网关序列(生产者只需要关注所有的末端消费者节点的序列)
        updateGatingSequencesForNextInChain(barrierSequences, processorSequences);

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

	/**
	 * 更新网关序列(当消费者链后端添加新节点时)
	 * @param barrierSequences 新节点依赖的网关序列(屏障序列),这些序列有了后继节点,就不再是网关序列了
	 * @param processorSequences 新增加的节点的序列,新增的在消费者链的末端,因此它们的序列就是新增的网关序列
	 */
    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);
        }
    }
}

EventHandlerGroup代码分享如下,可以看到,内部存入了当前消费者的所有Sequence,用来创建下一个节点的SequenceBarrier,串联动作通过then实现,并联动作通过add实现:

/**
 * 事件处理器组,作为Disruptor构成中的一部分。
 * <b>这个类很重要</b>
 *
 * 用于组织消费者之间的依赖关系。
 * 建立消费者之间的依赖其实也就是建立消费者与前驱节点的Sequence之间的依赖
 * 它会建立依赖消费者之间的依赖,也就是Barrier中的dependentSequence的由来。
 */
public class EventHandlerGroup<T>
{
	/**
	 * 方便回调,用于创建具有依赖关系的消费者
	 * {@link Disruptor#createEventProcessors(Sequence[], EventHandler[])}
	 * {@link Disruptor#createEventProcessors(Sequence[], EventProcessorFactory[])}
	 *
	 */
    private final Disruptor<T> disruptor;
	/**
	 * 所有消费者的信息,方便增加和查询
	 * (包含所有EventHandler的信息)
	 */
    private final ConsumerRepository<T> consumerRepository;
	/**
	 * 当前EventHandlerGroup拥有的所有Sequence。
	 * 也隐含的表示了EventHandlerGroup所代表的所有消费者。
	 * 也就是EventHandlerGroup的所有EventHandler的后继消费者的依赖Sequences
	 * 其它代码里面的 dependentSequence / barrierSequences / sequencesToTrack 其实就是它啦。
	 */
    private final Sequence[] sequences;

    EventHandlerGroup(
        final Disruptor<T> disruptor,
        final ConsumerRepository<T> consumerRepository,
        final Sequence[] sequences)
    {
        this.disruptor = disruptor;
        this.consumerRepository = consumerRepository;
        this.sequences = Arrays.copyOf(sequences, sequences.length);
    }

    /**
	 * 创建一个合并了给定group的新的EventHandlerGroup。
	 * 用于建立消费者与合并后的group的sequence的依赖。
     */
    public EventHandlerGroup<T> and(final EventHandlerGroup<T> otherHandlerGroup)
    {
        final Sequence[] combinedSequences = new Sequence[this.sequences.length + otherHandlerGroup.sequences.length];
        System.arraycopy(this.sequences, 0, combinedSequences, 0, this.sequences.length);
        System.arraycopy(
            otherHandlerGroup.sequences, 0,
            combinedSequences, this.sequences.length, otherHandlerGroup.sequences.length);
        return new EventHandlerGroup<>(disruptor, consumerRepository, combinedSequences);
    }

    
    public EventHandlerGroup<T> and(final EventProcessor... processors)
    {
        Sequence[] combinedSequences = new Sequence[sequences.length + processors.length];

        for (int i = 0; i < processors.length; i++)
        {
            consumerRepository.add(processors[i]);
            combinedSequences[i] = processors[i].getSequence();
        }
        System.arraycopy(sequences, 0, combinedSequences, processors.length, sequences.length);

        return new EventHandlerGroup<>(disruptor, consumerRepository, combinedSequences);
    }

    /**
	 * 添加一批EventHandler从RingBuffer中消费事件。
	 *
	 * 这些新增的EventHandler只能消费已经被当前EventHandlerGroup代表的所有消费者已经消费的事件。
	 *
	 * 这个方法对于构建链式消费来说很有用,会确立明确的先后顺序。
     */
    @SafeVarargs
    public final EventHandlerGroup<T> then(final EventHandler<? super T>... handlers)
    {
        return handleEventsWith(handlers);
    }

    /**
	 * 利用EventProcessorFactory添加一批EventProcessor从RingBuffer中消费数据。
	 * 这些新增的Processor只能消费已经被当前EventHandlerGroup代表的所有消费者已经消费的事件。
	 *
	 * 这个方法对于构建链式消费来说很有用,会确立明确的先后顺序。
     */
    @SafeVarargs
    public final EventHandlerGroup<T> then(final EventProcessorFactory<T>... eventProcessorFactories)
    {
        return handleEventsWith(eventProcessorFactories);
    }

    /**
	 * 创建一个WorkerPool消费从RingBuffer中消费事件。
	 * WorkerPool只会消费已经被当前EventHandlerGroup所代表的所有消费者已经消费的事件。
	 * 且每一个事件只会被WokerPool中的一个WorkHandler处理。
	 *
	 * 这个方法对于构建链式消费来说很有用,会确立明确的先后顺序。
     */
    @SafeVarargs
    public final EventHandlerGroup<T> thenHandleEventsWithWorkerPool(final WorkHandler<? super T>... handlers)
    {
        return handleEventsWithWorkerPool(handlers);
    }

    /**
	 * 添加一批EventHandler从RingBuffer中消费事件。
	 *
	 * 这些新增的EventHandler只能消费已经被当前EventHandlerGroup代表的所有消费者已经消费的事件。
	 * 这个方法对于构建链式消费来说很有用,会确立明确的先后顺序。
     */
    @SafeVarargs
    public final EventHandlerGroup<T> handleEventsWith(final EventHandler<? super T>... handlers)
    {
        return disruptor.createEventProcessors(sequences, handlers);
    }

    /**
	 *
	 * 利用EventProcessorFactory添加一批EventProcessor从RingBuffer中消费数据。
	 * 这些新增的Processor只能消费已经被当前EventHandlerGroup代表的所有消费者已经消费的事件。
	 *
	 * 这个方法对于构建链式消费来说很有用,会确立明确的先后顺序。
     */
    @SafeVarargs
    public final EventHandlerGroup<T> handleEventsWith(final EventProcessorFactory<T>... eventProcessorFactories)
    {
        return disruptor.createEventProcessors(sequences, eventProcessorFactories);
    }

    /**
	 * 创建一个WorkerPool消费从RingBuffer中消费事件。
	 * WorkerPool只会消费已经被当前EventHandlerGroup所代表的所有消费者已经消费的事件。
	 * 且每一个事件只会被WokerPool中的一个WorkHandler处理。
	 *
	 * 这个方法对于构建链式消费来说很有用,会确立明确的先后顺序。
     */
    @SafeVarargs
    public final EventHandlerGroup<T> handleEventsWithWorkerPool(final WorkHandler<? super T>... handlers)
    {
        return disruptor.createWorkerPool(sequences, handlers);
    }

    /**
     * Create a dependency barrier for the processors in this group.
     */
    public SequenceBarrier asSequenceBarrier()
    {
        return disruptor.getRingBuffer().newBarrier(sequences);
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值