Disruptor是一个由LMAX开源的Java并发框架。LMAX是一种新型零售金融交易平台,这个系统是创建在 JVM 平台上,核心是一个业务逻辑处理器,它可以在一个线程里每秒处理 6 百万订单。业务逻辑处理器彻底是运行在内存中(in-memory),使用事件源驱动方式(event sourcing),具备低延迟,高吞吐的特性。html
disruptor有多快?官方给出了和ArrayBlockingQueue的比较图表:git
Disruptor能够用来解决并发编程中的一个广泛的问题: 消息队列的处理(producer和consumer)。github
Disruptor 相对于传统方式的优势:算法
在了解disruptor如何工做以前,咱们先看一下disruptor一些重要组件的介绍(翻译自官方文档,略有修改):编程
将这些元素放入Disruptor的context中,Disruptor的总体结构图以下:数组
多播事件缓存
Queue和Disruptor之间最大的差别。当有多个消费者监听在同一Disruptor的全部事件,一个单一的事件只会被发送到一个单一的消费者。Disruptor一个使用的case是当你须要对一样的数据进行不同的操做的时候。LMAX典型的例子是,咱们有三个操做,日志(输入数据写入持久性日志文件),复制(将输入数据发送到另外一台机器以确保有数据的远程复制),和业务逻辑(实际处理工做)。普通的Executor-style处理,多是利用WorkPool并行的来处理这些不一样的事件。这样却不是实现这个目标最有效的途径。安全
如上图所示,咱们有三个EventHandler(JournalConsumer, ReplicationConsumer and ApplicationConsumer)监听着Disruptor,每个Handler都会顺序的收到Disruptor里全部可用的消息,这样就使得这些消费者能够并行的处理这些消息了。性能优化
为了支持现实中并行处理的应用,必须支持消费者之间的协调。回到上面的例子,防止业务逻辑的消费还在继续,日志和复制的消费者已经完成了他们的任务是必须的。咱们把这个概念称为门,或者更准确地说,这个行为的超级集合的特征叫作门。门发生在两个地方。首先,咱们须要确保生产者不超过消费者。这是经过添加有关消费者到Disruptor时经过调用RingBuffer.addgatingconsumers() 实现的。其次,经过实现一个SequenceBarrier(内存屏障)的结构能够实现必须先完成某些操做的需求。服务器
参考图1,有三个消费者监听唤醒队列中的事件,在图中有一个依赖图,ApplicationConsumer依赖于 JournalConsumer 和 ReplicationConsumer,这就说明 JournalConsumer 和 ReplicationConsumer能够互相自由的并发,这层依赖关系能够从 ApplicationConsumer的 SequenceBarrier链接到 JournalConsumer和 ReplicationConsumer的 Sequences看出来。值得注意的是 Sequencer和下游消费者之间的关系。做用之一就是确保发布不会覆盖Ring Buffer。为了作到这一点,下游消费者没有一个序列比RingBuffer的Sequence还要小,比RingBuffer的size还要小,然而,利用这个依赖图能够作一些有意思的操做,由于ApplicationConsumers Sequence是小于JournalConsumer 和 ReplicationConsumer(这就是依赖图所保证的),Sequencer只用关注ApplicationConsumer的Sequence便可,其实通常意义上,Sequencer只用知道消费者的Sequences依赖树中的叶子节点便可。
事件预分配
Disruptor的设计的一个目标就是能被用在一个低延迟的环境中。在低延迟系统中,必须减小或移除内存分配操做,基于Java开发的目的就是减小垃圾回收。(在低延迟的C/C++系统中,大内存分配也存在问题,由于内存分配器也会存在竞争)
为了实现低延迟,Disruptor容许用户对事件的内存进行预分配,在构造过程和用户提供的EventFactory中都会在Disruptor 的 RingBuffer中为每一个实体分配。当发布新数据到Disruptor中,API就会容许用户获取构造方法的对象,以致于能够调用方法或者更新字段。Disruptor对这些操做提供并发安全性的保障。
可选的无锁操做
另外一个关键的实现低延迟的细节就是在Disruptor中利用无锁的算法,全部内存的可见性和正确性都是利用内存屏障或者CAS操做。使用CAS来保证多线程安全,与大部分并发队列使用的锁相比,CAS显然要快不少。CAS是CPU级别的指令,更加轻量,没必要像锁同样须要操做系统提供支持,因此每次调用不须要在用户态与内核态之间切换,也不须要上下文切换。
只有一个用例中锁是必须的,那就是BlockingWaitStrategy(阻塞等待策略),惟一的实现方法就是使用Condition实现消费者在新事件到来前等待。许多低延迟系统使用忙等待去避免Condition的抖动,然而在系统忙等待的操做中,性能可能会显著下降,尤为是在CPU资源严重受限的状况下,例如虚拟环境下的WEB服务器。
参考资料:
LMAX Disruptor
Spark性能优化指南——基础篇- - 美团点评技术团队
Disruptor入门