前言
理解Disruptor的最佳方式是,将其与一些容易理解和目的相似的东西比较。这里的参照物就是java里的阻塞队列(BlockingQueue)。
与BlockingQueue的异同:
同:目的相同,都是为了在同一进程的线程间传输数据。
异:对消费者多播事件;预分配事件内存;可选无锁。
核心概念
- Ring Buffer : 曾经的核心。自从3.0以上,环形缓冲器只作为Disruptor存储和更新数据(事件)的容器。对于一些高级用法,可以完全替换为用户提供的容器。
- Sequence:Disruptor使用Sequence作为一种确定特定组件位置的方法。每个消费者(EventProcessor)都维护一个Sequence,Disruptor自己也是一样。大部分并发代码以来这些Sequence值的移动,因此Sequence支持AtomicLong的当前许多特性。事实上,两者唯一的区别是Sequence包含了附加功能来防止Sequence和其他值的伪共享。
- Sequencer:Disruptor的真正核心。此接口的两个实现(单生产者和多生产者)都实现了用于在生产者和消费者间快速准确传递数据的并发算法。
- Sequence Barrier:由Sequencer产生,持有Sequencer的主要发布Sequence和任意独立消费者的Sequence的索引。它包含判断是否有可供消费者处理的可用事件的逻辑。
- Wait Strategy:等待策略决定了一个消费者如何等待生产者发布到Disruptor的事件。
- Event:生产者传递给消费者的数据单元。用户自定义。
- EventProcessor:处理Disruptor事件的主要循环,拥有消费者的Sequence。有一个BatchEventProcessor包含了一个事件循环的高效实现,会在事件可用时回调用户提供的EventHandler接口实现。
- EventHandler:用户实现接口,代表Disruptor的一个消费者。
- Producer:用户调用Disruptor进行入队的代码。在框架中没有代码表示。
多播事件
这是queue和Disruptor最大的行为区别。队列中的一个事件只能被一个消费者消费,而Disruptor中的时间会发布给所有消费者。这是由于Disruptor意图处理同一数据的独立并行处理操作(译注:类似JMS的topic模式)。比如LMAX中同一数据需要进行记录日志、复制和业务逻辑操作。当然,在Disruptor中同时并行处理不同事件可以使用WorkerPool(译注:类似JMS的queue模式中的多消费者实现)。但需要注意的是,由于这种特性并非是Disruptor的首要工作,所以使用WorkerPool可能并不是最高效的做法。
查看上图,三个消费者JournalConsumer、ReplicationConsumer和ApplicationConsumer将会以相同顺序接收Disruptor所有可用消息。这实现了这些消费者的并行工作。
消费者依赖图
为了支持并发处理的真实世界应用,很有必要支持消费者间的协调工作。回顾上图,在日志记录和复制消费者完成工作前,有必要阻止业务逻辑消费者的进一步工作。我们称这个概念为gating,更准确的说是这种行为的超集称为gating。Gating发生在两个地方:第一用来保证生产者不能超过消费者。这通过调用RingBuffer.addGatingConsumers()把相关消费者添加到Disruptor实现。第二,先前提到的情况是通过从必须首先完成其处理的组件构造包含序列的SequenceBarrier来实现的。
回顾图1,有三个消费者监听RingBuffer的事件。在这个例子中,有一个依赖图。ApplicationConsumer依赖JournalConsumer和ReplicationConsumer。这意味着JournalConsumer和ReplicationConsumer可以相互并行运行。依赖关系可以从ApplicationConsumer的SequenceBarrier和JournalConsumer及ReplicationConsumer的Sequence观察到。同时引起注意的是Sequencer和下游消费者的关系。它的一个角色是保证发布不会环绕RingBuffer。为了做到这点,下游消费者的Sequence不能小于RingBuffer的Sequence。然而,使用依赖图会发生一个有趣的优化。由于ApplicationConsumer Sequence保证小于等于JournalConsumer和ReplicationConsumer(由依赖关系保证),Sequencer只需要观察ApplicationConsumer的Sequence。从广义上来说,Sequencer只需要关注依赖树种叶子节点的消费者Sequence。
事件预分配
Disruptor的一个目标是可以用于低延迟环境中。在低延迟系统中,有必要减少或消除内存分配。在Java系统中,目标是减少由于垃圾回收造成的停顿次数(在低延迟的C/C++系统中,重内存分配会由于内存分配器的征用也可能导致问题)。
为了支持这个目标,用户可以预分配Disruptor中事件的存储。用户提供的EventFactory会在Disruptor中RingBuffer每个条目构建时调用。当发布新数据到Disruptor时,有API供用户调用来持有构建出的对象,这样可以调用对象的方法或更新对象属性。在正确实现下,Disruptor保证这些操作操作是并发安全的。