Contents
  1. 1. Disruptor 是什么?
    1. 1.1. 机械和谐(或者翻译为情怀、同情)
    2. 1.2. 为什么不用队列

本篇文章目的在于介绍 LMAX Disruptor,探讨它是如何帮助我们实现软件低延迟、高并发特性。

Disruptor 是什么?

Disruptor 是个基于Java的开源并发编程框架,由 LMAX 编写。通过更高效的利用底层硬件的设计实现,能够高效的优化性能。它能处理大量事务,而且低延迟(然而并不会像常规并发代码那样复杂)。

机械和谐(或者翻译为情怀、同情)

让我们从机械和谐的核心概念开始 - 这就是了解底层硬件的工作方式和编程方式,从而以最适合该硬件的方式做自己的工作。

反正不明嚼栗。比如让我们看看CPU和内存组织能够如何影响软件性能。我们都知道CPU与主内存之间具有多层缓存,当CPU执行操作时,它首先在L1中查找数据,然后在L2中查找,然后在L3中查找,最后在主存储器中查找。 缓存级别越低,操作所需的时间就越长(参考下表)。如果对一条数据多次执行相同的操作(例如,循环计数器),则将数据加载到非常靠近CPU的位置就很有意义(减少时间)。

距离CPU的位置 CPU时钟 耗时
主内存 Multiple(多个) ~60-80 ns
L3缓存 ~40-45 时间片 ~15 ns
L2缓存 ~10 时间片 ~3 ns
L1缓存 ~3-4 时间片 ~1 ns
寄存器 1 时间片 非常非常快

为什么不用队列

队列实现倾向于在head,tail和size变量上进行写争用,由于消费者和生产者之间的速度存在差异,队列通常总是接近满(生产者快)或接近空(消费者快)。队列的中间生产率和消费率平均匹配,但这需要保持生产者和消费者的平衡,这是极少能实现的。为了处理读写,队列通常会使用锁,这可能导致内核级别的上下文切换,这个时候所涉及的处理器可能会丢失其缓存中的数据。所以为了获得最佳的缓存行为,就要设计应只有一个内核级别的缓存操作(读取可以有多个)。队列无法遵循“单写者”原则。如果两个单独的线程正在写入两个不同的值,则每个操作都会使另一个的缓存行无效。即使两个线程正在写两个不同的变量,这也是两个线程之间的写竞争。这被称为不正当的共享,因为每次访问头部时,尾部也会被访问,反之亦然。

2.3. Disruptor是如何工作的?
image
Disruptor 有一个基于数组的循环数据结构(环装缓冲区)。这个循环数据结构,它是个拥有下个可用元素引用的数组。预先分配了对象内存空间。生产者与消费者通过这个循环数据在不加锁或者竞争的情况下进行读写操作。

在Disruptor 中,所有事件(events)以组播的方式被发布给所有消费者,以便下游队列通过并行的方式进行消费。因为消费者是并行处理的,所以需要协调他们之间的依赖关系(依赖性图)。

生产者和消费者都有个序列计数器,用于指示缓冲区中当前正在被它所处理的元素。所有生产者或消费者都只可以修改它自己的序列计数器,但可以读取其他的序列计数器。生产组和消费者读取计数器的时候要确保写入不会被锁住。

Contents
  1. 1. Disruptor 是什么?
    1. 1.1. 机械和谐(或者翻译为情怀、同情)
    2. 1.2. 为什么不用队列