本文翻译自Disruptor在github上的wiki文章Introduction,原文能够看这里。git
做为程序猿大多数都有对技术的执着,想在这个方面有所提高。对于优秀的事物保持积极学习的心态,并发编程是开发中一大难题,不管是底层的各类理论仍是上层的各类关于并发组件的实现,都很是的晦涩难懂。并发之因此难,就是由于"多"而难以控制。大多数都会使用"锁"这种技术进行控制,可是"锁"这种技术每每和性能又是背道而驰。为了可以将性能最大化,无锁 去锁显然是提高并发性能的关键。前人的智慧还真是慧深莫测,还真在这块领域有所突破,用一种不一样以往的设计方式实现了无锁的高性能并发队列 — disruptor。github
本系列就对disruptor进行学习:算法
系列将从以上几个方面由浅入深,层层递进式学习。disruptor是open source的,源代码被托管在github上LMAX-Exchange/disruptor。编程
其中wiki有不少很是优秀的文章介绍,是很是不错的学习资源。但都是英文,索性这里将其介绍篇和使用篇翻译一遍。安全
LMAX Disruptor是高性能的线程内部通讯的消息库。它源于LMAX对并发、性能、非阻塞算法的研究,目前已经成为LMAX的交易基础设施的核心部分。服务器
理解Disruptor是什么的最好方式就是将其与现有的比较好理解的东西比较。Disruptor就至关于Java中BlockingQueue。同队列同样,Disruptor的目的就是在同一进程的线程间进行数据交互。然而Disruptor又提供了一些关键的不一样于队列的特征:并发
在理解Disruptor如何工做以前,定义一些广泛存在文档和源代码中的术语是很是有价值的。对于倾向DDD的人而言,它们就是Disruptor领域的无处不在的语言。性能
Ring Buffer(环形缓冲):RingBuffer是Disruptor的主要概念。但从Diruptor 3.0开始,RingBuffer只负责存储和更新Disruptor的数据。一些高级的使用场景,能够被用户替换。学习
Sequence(序列):Disruptor使用Sequence来标记特定组件到达什么位置的。每一个消费者(EventProcessor)内部都维护一个Sequence来标记本身消费到的位置。大多数并发代码都是依赖于Sequence的移动,所以Sequence支持大量AtomicLong的特征。实际上二者之间的不一样在于Sequence实现了额外的阻止伪共享的功能。优化
Sequencer(序列器):Sequencer是Disruptor中的实际核心。其中两个实现(Single producer,multi producer)全都实现了在生产者与消费者间进行快速正确的传递数据的算法。
Sequence Barrier(sequence屏障):Sequence Barrier由Sequencer建立,它包含了来自Sequencer的已经发布的主要sequence的引用,或者包含了依赖的消费者的sequence。同时也包含决定是否有时间可达共消费者处理的逻辑。
Wait Strategy(等待策略):Wait Strategy决定了消费者以何种方式等待生产者将事件放进Disruptor。
Event Processor(事件处理器):处理来自Disruptor的事件的主要事件循环,且包含了消费者的sequence。有一个实现为BatchEventProcessor,包含了高效的事件循环,将不断回调被提供的EventHandler接口。
EventHandler(事件处理逻辑):一个接口,由用户实现定义消费者的处理逻辑。
Producer(生产者):这也是用于实现的用户代码,调用Disruptor入队事件。没有任何代码规约。
为了可以将这些概念关联起来,即放在一个上下文中表示,下图是个例子,展现LMAX在高性能的核心服务中使用Disruptor的场景:
Note:
原文中没有对这张图进行解释,笔者看到时一脸懵逼,也是在全局的了解了Disruptor后再来研究这张图,才弄明白其含义。这张图可谓是将Disruptor描绘的淋漓尽致。
这里笔者也不对其作过多的介绍,由于要弄懂这张图,势必要对Disruptor有个总体理解。后面介绍原理时再细致解析。
总体而言,Producer生产事件放入RingBuffer中,Consumer利用Sequence Barrier和自身包含的Sequence从RingBuffer中获取可消费的事件。
这是队列和Disruptor之间的一个巨大的行为差别。当你有多个消费者监听在相同的Disruptor上是,全部的事件将都被发送给全部的消费者,这点与队列不一样,在队列中,一个时间将只发送给一个消费者。Disruptor更偏向用在当有多个无依赖的消费者并行处理相同数据的场景中。在LMAX中有个很是经典的案例,有三个操做journalling(写输入的数据到一个持久化的日志文件),replication(发送输入数据到另外一台机器,确保远程备份)和业务逻辑处理。相似Executor风格的事件处理,在这里同时并行的处理不一样的事件,可使用WorkePool。
再看上图,有三个事件处理器(JournalConsumer, ReplicationConsumer和 ApplicationConsumer)监听Disruptor,每个都将接受Disruptor中全部的可用消息。这个容许三个消费者并行的工做。
为了支持并行处理行为的实际应用,支持消费者以前的协调是颇有必要的。再以上述例子来讲,业务逻辑的处理必须在journalling和replication完成以后。咱们把这种概念叫作gating,或者更准确的说这个行为特征的超集被称为gating。Disruptor中,Gating发生在两个地方。第一,咱们须要确保生产者不要超过消费者。经过调用RingBuffer.addGatingConsumers()增长相关的消费者至Disruptor来完成。第二,就是以前所说的场景,经过构造包含须要必须先完成的消费者的Sequence的SequenceBarrier来实现。
引用上图来讲,有三个消费者监听来自RingBuffer的事件。在这个例子中,有一个依赖关系图。ApplicationConsumer依赖JournalConsumer和ReplicationConsumer。这个意味着JournalConsumer和ReplicationConsumer能够自由的并发运行。依赖关系能够当作是从ApplicationConsumer的SequenceBarrier到JournalConsumer和ReplicationConsumer的Sequence的链接。还有一点值得关注,Sequencer与下游的消费者之间的关系。它的角色是确保发布不会包裹RingBuffer。为了作到这点,下游消费者的Sequence没有一个是低于RingBuffer的Sequence而不是RingBuffer的大小。而后使用依赖关系时,一个有趣的优化可使用。由于ApplicationConsumers的Sequence被保证是低于或者等于JournalConsumer和ReplicationConsumer的Sequence,因此Sequencer只须要检查ApplicationConsumers的Sequence。在更为广泛的应用场景中,Sequencer只须要意识到消费者树中的叶子节点的的Sequence便可。
Disruptor的一个目标之一是被用在低延迟的环境中。在低延迟系统中,必需要减小或者去除内存分配。在基于Java的系统中,须要减小因为GC致使的停顿次数(在低延迟的C/C++系统中,因为内存分配器的争用,大量的内存分配也会致使问题)。
为了知足这点,用户能够在Disruptor中为事件预分配内存。在构造期间,EventFactory由用户提供,并将在Disruptor的RingBuffer中为每一个条目调用。当发布新的数据至Disruptor时,API容许用户获取已经被构造的对象,以即可以调用方法或者更新在该对象的域。Disruptor将确保这些操做是线程安全。
对于低延迟的需求又推进了另外一个关键性是普遍的使用无锁算法实现Disruptor。全部的内存可见性和正确性都使用内存屏障和CAS操做实现。只仅仅一个场景BlockingWaitStrategy中使用到了lock。这仅仅是为了使用条件,以便在等待新事件到达时停放消耗线程。许多低延迟系统都使用忙等来避免使用Condition形成的抖动。可是忙等的数量将会致使性能的降低,特别是CPU资源严重受限的状况下。例如,在虚拟环境中的Web服务器。