了解Disruptor

13 篇文章 0 订阅

了解Disruptor

Disruptor是一个单机版最高性能消息队列,最早由金融行业中发明,它的GitHub地址:
https://github.com/LMAX-Exchange/disruptor/wiki/Introduction
本文主要对GitHub上的英文介绍进行简单翻译记录。

介绍

Michael Barker edited this page on 2 Mar 2015 · 8 revisions

了解Disruptor的最好办法是,和一些已经很了解的并且有相似目的的东西来比较。Disruptor在java中就相当于BlockingQueue。 与队列类似,Disruptor的目的是不断重复在多线程之间移动数据 (例如:消息或事件)这个过程.。然而Disruptor 提供了一些关键性的特性,是与队列不同的。他们是:

  • Multicast events to consumers, with consumer dependency graph. 向使用者多播事件,具有使用者依赖关系图。
  • Pre-allocate memory for events. 为事件预先分配内存。
  • Optionally lock-free. 可以选择无锁。

核心概念

在了解Disruptor怎么工作之前,需要先定义一些术语,它们会对我们阅读文档或者代码时,发挥很大的作用。对于那些对DDD感兴趣的人来说,可以把它看作是Disruptor 领域中无处不在的语言。

  • Ring Buffer: Ring Buffer是Disruptor经常被考虑的方向,然而在3.0之前 Ring Buffer 仅仅负责通过Disruptor存储和更新数据。对于一些高级的用例,可以完全由用户来代替。
  • Sequence: Disruptor 使用Sequences作为辨别一个特定组件位置的手段。 每一个消费者(事件处理器),就像Disruptor自己一样,都会维护一个Sequence。 大多数并发代码不需要依赖于这些Sequence值的运作,因此Sequence支持许多AtomicLong的并发特性。实际上,两者之间的最大不同是,Sequence包含了额外的功能来防止序列和其他值之间的错误共享。
  • Sequencer: Sequencer(定序器)是Disruptor的真正核心。这个接口的两个实现(single producer, multi producer) ,实现了所有用于在生产者和消费者之间正确传递数据的,快速并发算法。
  • Sequence Barrier: Sequence Barrier(序列屏障)由 Sequencer产生,并且包含来自Sequencer的主发布序列和依赖于消费者的序列的引用。它包含确定是否有任何事件可供消费者处理的逻辑。
  • Wait Strategy: Wait Strategy(等待策略) 决定消费者如何等待一个被producer放置到Disruptor 中的事件。更多的详细信息可以在关于可选的无锁部分中找到。
  • Event: (事件)从生产者传递给消费者的数据单位。事件没有特定的代码表示,因为它完全是由用户定义的。
  • EventProcessor: (事件处理器)Disruptor 中主要用来循环处理事件的组件,并拥有消费者序列的所有权。有一个单独的表示BatchEventProcessor,它包含事件循环的有效实现,并将回调到EventHandler接口的使用提供的实现。
  • EventHandler: (事件处理程序)由用户实现并代表Disruptor的消费者的接口。
  • Producer: (生产者)这是调用Disruptor 以加入事件队列的用户代码。这个概念在代码中也没有表示。

为了将这些元素放到上下文中,下面是LMAX如何在其高性能核心服务(例如exchange)中使用干扰器的一个例子。

图1所示。有一群依赖的消费者的Disruptor。
在这里插入图片描述

多播事件

这是队列和Disruptor之间最大的行为差异。当您有多个消费者听相同的Disruptor时,与只将单个事件发送到单个使用者的队列相反,所有事件都会发布给所有消费者。Disruptor的行为将用于在需要对同一数据进行独立的多个并行操作的情况下使用。LMAX的典型示例是三个操作:日志记录(将输入数据写入持久日志文件)、复制(将输入数据发送到另一台计算机以确保存在数据的远程副本)和业务逻辑(真正的处理工作)。也可以使用工作池。注意,它被栓在现有Disruptor类之上,并且没有得到相同的第一类支持,因此它可能不是实现该特定目标的最有效方法。

查看图1,可以看到有3个事件处理程序在侦听Disruptor(JournalConsumer、ReplicationConsuer和ApplicationConsuer),每个事件处理程序都将接收Disruptor中可用的所有消息(顺序相同)。这使得每个消费者都可以并行工作。

消费者依赖图

为了支持并行处理行为的实际应用,有必要支持消费者之间的协调。回到上面描述的示例,必须防止业务逻辑使用者在日志和复制使用者完成其任务之前取得进展。我们把这个概念称为门控,或者更准确地说,这种行为的一个超级集合的特性称为选通。门在两个地方发生。首先,我们需要确保生产者不超过消费者。这是通过调用RingBuffer.addGatingConsumers()将相关的使用者添加到Disruptor来处理的。其次,前面提到的情况是通过从必须首先完成处理的组件中构造包含序列的SequenceBarrier来实现的。

参考图1,有3个使用者正在监听来自ring缓冲区的事件。在这个例子中有一个依赖关系图。ApplicationConsumer 依赖于JournalConsumer 和ReplicationConsumer。这意味着JournalConsumer 和ReplicationConsumer可以自由地并行运行。依赖关系可以通过ApplicationConsumer 的序列障碍到JournalConsumer 和ReplicationConsumer序列的连接来表现出来。同样值得注意的是,Sequencer与下游消费者之间的关系。其作用之一是确保发布不包装环形缓冲区。要做到这一点,下游使用者中没有一个序列可能低于环形缓冲区的序列,小于环形缓冲区的大小。然而,使用依赖关系图可以进行有趣的优化。由于保证ApplicationCustomers序列小于或等于JournalConsumerand和ReplicationConsuer(这就是依赖关系所确保的),Sequencer只需要查看ApplicationConsuer的序列。在一般的意义上,Sequencer只需要知道使用者的序列,即依赖树中的叶节点。

事件预分配

Disruptor的目标之一是允许在低延迟环境中使用。在低延迟系统中,有必要减少或删除内存分配。在基于Java的系统中,其目的是减少垃圾收集(在低延迟的C/C++系统中,由于内存分配程序上的争用,大量内存分配也是有问题的)。

为了支持这一点,用户可以预先分配干扰器中事件所需的存储。在构建和EventFactory期间,由用户提供,并将调用每个条目在Disruptor的环形缓冲区。当向Disruptor发布新数据时,API将允许用户获取构造的对象,以便他们能够调用方法或更新存储对象上的字段。Disruptor保证只要正确实现这些操作都是并发安全的。

可选择无锁

另一个关键的实现细节是广泛使用无锁算法来实现Disruptor。所有的内存可见性和正确性保证都是使用内存屏障和/或比较交换操作来实现的。只有一个用例需要一个实际的锁,那就是在BlockingWaitStrategy中。这样做完全是为了使用一个条件,以便在等待新事件到达时,可以停放一个消耗线程。许多低延迟系统将使用繁忙等待,以避免使用一个条件而引起的抖动,但是在系统繁忙的数量中,等待操作会导致性能显著下降,特别是在CPU资源受到严重限制的情况下。例如,虚拟环境中的Web服务器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值