Concurrency LMAX';什么是破坏者模式工作?

Concurrency LMAX';什么是破坏者模式工作?,concurrency,latency,actor,disruptor-pattern,Concurrency,Latency,Actor,Disruptor Pattern,我正在努力理解这个问题。我看了InfoQ视频,并试图阅读他们的论文。我知道这涉及到一个环形缓冲区,它被初始化为一个非常大的数组,以利用缓存的局部性,消除新内存的分配 听起来好像有一个或多个原子整数可以跟踪位置。每个“事件”似乎都有一个唯一的id,它在环中的位置是通过找到它相对于环大小的模数来确定的,等等 不幸的是,我对它的工作原理没有直觉。我做过很多交易申请,研究过SEDA等 在他们的演讲中,他们提到这种模式基本上就是路由器的工作原理;然而,我也没有找到任何关于路由器工作原理的好描述 有没有更好

我正在努力理解这个问题。我看了InfoQ视频,并试图阅读他们的论文。我知道这涉及到一个环形缓冲区,它被初始化为一个非常大的数组,以利用缓存的局部性,消除新内存的分配

听起来好像有一个或多个原子整数可以跟踪位置。每个“事件”似乎都有一个唯一的id,它在环中的位置是通过找到它相对于环大小的模数来确定的,等等

不幸的是,我对它的工作原理没有直觉。我做过很多交易申请,研究过SEDA等

在他们的演讲中,他们提到这种模式基本上就是路由器的工作原理;然而,我也没有找到任何关于路由器工作原理的好描述


有没有更好的解释呢?

谷歌代码项目在环形缓冲区的实现方面做得很好,但是对于想要了解它是如何工作的人来说,这有点枯燥、学术和困难。然而,也有一些博客文章已经开始以更易读的方式解释内部内容。有一个是中断器模式的核心,一个(与从中断器读取相关的部分)和一些可用的

对破坏者最简单的描述是:它是一种以最有效的方式在线程之间发送消息的方法。它可以作为队列的替代品,但它也与SEDA和Actors共享许多特性

与队列相比:

破坏者提供了将消息传递到另一个线程的能力,在需要时将其唤醒(类似于阻塞队列)。然而,有三个明显的区别

  • Disruptor的用户通过扩展Entry类并提供工厂来进行预分配来定义消息的存储方式。这允许内存重用(复制),或者条目可能包含对另一个对象的引用
  • 将消息放入中断器是一个两阶段的过程,首先在环形缓冲区中声明一个插槽,该插槽为用户提供可以填充适当数据的条目。然后必须提交条目,这两个阶段的方法是必要的,以允许灵活使用上述内存。正是提交使消息对使用者线程可见
  • 消费者有责任跟踪从环形缓冲区中消费的消息。将此职责从环形缓冲区本身移开有助于减少写入争用量,因为每个线程都维护自己的计数器
  • 与演员相比

    与大多数其他编程模型相比,Actor模型更接近于破坏者,特别是如果您使用提供的BatchConsumer/BatchHandler类。这些类隐藏了维护使用序列号的所有复杂性,并在发生重要事件时提供了一组简单的回调。然而,有两个细微的区别

  • Disruptor使用1线程1消费者模型,参与者使用N:M模型,也就是说,您可以有任意多的参与者,他们将分布在固定数量的线程中(通常每个核心1个)
  • BatchHandler接口提供了一个额外的(非常重要的)回调
    onEndOfBatch()
    。这允许较慢的使用者,例如,那些进行I/O以将事件批处理在一起以提高吞吐量的使用者。在其他Actor框架中进行批处理是可能的,但是几乎所有其他框架都不在批处理结束时提供回调,因此需要使用超时来确定批处理的结束,从而导致较差的延迟
  • 与SEDA相比

    LMAX构建了干扰器模式,以取代基于SEDA的方法

  • 与SEDA相比,它提供的主要改进是并行工作的能力。为此,破坏者支持将相同的消息(以相同的顺序)多播给多个消费者。这避免了在管道中需要分叉阶段
  • 我们还允许消费者等待其他消费者的结果,而不必在他们之间设置另一个排队阶段。消费者只需查看其所依赖的消费者的序列号即可。这避免了在管道中连接阶段的需要
  • 与记忆障碍相比


    另一种方式是把它看作一个结构化、有序的记忆屏障。生产者屏障形成写屏障,消费者屏障形成读屏障。

    首先,我们想了解它提供的编程模型

    有一个或多个作者。有一个或多个读卡器。有一行条目,完全按照从旧到新的顺序排列(图中为从左到右)。编写器可以在右端添加新条目。每个读卡器从左到右顺序读取条目。显然,读者无法阅读过去的作家

    没有条目删除的概念。我使用“reader”而不是“consumer”来避免条目被消费的图像。然而,我们知道最后一个读卡器左边的条目是无用的

    一般来说,读者可以同时阅读和独立阅读。但是,我们可以声明读取器之间的依赖关系。读取器依赖关系可以是任意非循环图。如果读卡器B依赖于读卡器A,则读卡器B不能读取超过读卡器A的内容

    读卡器依赖性的产生是因为读卡器A可以对条目进行注释,而读卡器B依赖于该注释。例如,A对条目进行一些计算,并将结果存储在条目中的字段
    A
    。A然后继续,现在B可以读取条目,并存储
    A
    A的值。如果读卡器C不依赖于A,则C不应尝试读取
    A

    这确实是一个有趣的编程模型。无论性能如何,模型本身都可以
    setNewEntry(EntryPopulator);
    
    interface EntryPopulator{ void populate(Entry existingEntry); }
    
    package com.coralblocks.coralqueue.sample.queue;
    
    import com.coralblocks.coralqueue.AtomicQueue;
    import com.coralblocks.coralqueue.Queue;
    import com.coralblocks.coralqueue.util.MutableLong;
    
    public class Sample {
    
        public static void main(String[] args) throws InterruptedException {
    
            final Queue<MutableLong> queue = new AtomicQueue<MutableLong>(1024, MutableLong.class);
    
            Thread consumer = new Thread() {
    
                @Override
                public void run() {
    
                    boolean running = true;
    
                    while(running) {
                        long avail;
                        while((avail = queue.availableToPoll()) == 0); // busy spin
                        for(int i = 0; i < avail; i++) {
                            MutableLong ml = queue.poll();
                            if (ml.get() == -1) {
                                running = false;
                            } else {
                                System.out.println(ml.get());
                            }
                        }
                        queue.donePolling();
                    }
                }
    
            };
    
            consumer.start();
    
            MutableLong ml;
    
            for(int i = 0; i < 10; i++) {
                while((ml = queue.nextToDispatch()) == null); // busy spin
                ml.set(System.nanoTime());
                queue.flush();
            }
    
            // send a message to stop consumer...
            while((ml = queue.nextToDispatch()) == null); // busy spin
            ml.set(-1);
            queue.flush();
    
            consumer.join(); // wait for the consumer thread to die...
        }
    }