什么是 Disruptor?

Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现

性能远远高于传统的BlockingQueue容器

Disruptor使用观察者模式,主动将消息发送给消费者,而不是等消费者从队列中取,在无锁的情况下, 实现queue(环形, RingBuffer)的并发操作, 性能远高于BlockingQueue

Disruptor 的设计思想

环形数组结构

为了避免垃圾回收,使用数组,数组对处理器的缓存机制更加友好

数组长度为 2^n,通过位运算,加快定位速度,下标采用递增的方式,不用担心索引溢出

无锁设计

每个生产者或者消费者线程,会先申请可以操作的元素在数组中的位置,申请到之后,直接在该位置写入或者读取数据

Disruptor 实现生产消费模型

pom

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.2.1</version>
</dependency>

LongEvent

// 声明一个Event来包含需要传递的数据
public class LongEvent {
    private Long value;

    public Long getValue() {
        return value;
    }

    public void setValue(Long value) {
        this.value = value;
    }
}

LongEventFactory

// Event工厂
public class LongEventFactory implements EventFactory<LongEvent> {

    public LongEvent newInstance() {
        return new LongEvent();
    }
}

LongEventHandler

// 事件消费者
public class LongEventHandler implements EventHandler<LongEvent> {

    public void onEvent(LongEvent event, long sequence, boolean endOfBatch) throws Exception {
        System.out.println("消费者:"+event.getValue());
    }
}

LongEventProducer

public class LongEventProducer {
    private RingBuffer<LongEvent> ringBuffer;

    public LongEventProducer(RingBuffer<LongEvent> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }

    public void onData(ByteBuffer byteBuffer) {
        // 获取事件队列下标位置
        long sequence = ringBuffer.next();
        try {
            // 取出空队列
            LongEvent longEvent = ringBuffer.get(sequence);
            // 赋值
            longEvent.setValue(byteBuffer.getLong(0));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("生产者发送数据。。。");
            // 发送数据
            ringBuffer.publish(sequence);
        }
    }
}

Main

public class Main {
    public static void main(String[] args) {
        // 创建可缓存线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        // 创建工厂
        EventFactory eventFactory = new LongEventFactory();
        // 创建ringBufferSize
        int ringBufferSize = 1024 * 1024;
        // 创建disruptor
        // MULTI表示可以多个生产者
        Disruptor<LongEvent> longEventDisruptor = new Disruptor<LongEvent>(eventFactory, ringBufferSize, executorService, ProducerType.MULTI, new YieldingWaitStrategy());
        // 注册消费者
        longEventDisruptor.handleEventsWith(new LongEventHandler());
        // 启动
        longEventDisruptor.start();
        // 创建RingBuffer容器
        RingBuffer<LongEvent> ringBuffer = longEventDisruptor.getRingBuffer();
        // 创建生产者
        LongEventProducer longEventProducer = new LongEventProducer(ringBuffer);
        // 指定缓冲区大小
        ByteBuffer byteBuffer = ByteBuffer.allocate(8);
        for (int i = 0; i < 100; i++) {
            byteBuffer.putLong(0, i);
            longEventProducer.onData(byteBuffer);
        }
        executorService.shutdown();
        longEventDisruptor.shutdown();
    }
}

什么是 RingBuffer

它是一个环(首尾相接的环),作用是存储数据,实现不同线程之间的数据传输

RingBuffer 每块区是拥有一个序号的,这个序号指向环形数组结构的下一个可用元素

随着不断地写进了填充这个圆环,这个指针序号会不断地递增,直到绕过这个环

如果圆环满了,它会将金数据覆盖,如上图:现在12的区域的下个区域目前是3,如果有新的数据到来,那么指针往下移的时候就会把区域3的数据给覆盖变成13,框架提供了一系列帮助我们平行消费的监控,会很好的控制生产者和消费者之间的速度,从而达到生产和消费之间的平衡

RingBuffer 为什么效率高?

采用数组,数组支持索引访问

数组的内存分配是预先加载的,一但指定大小创建后,就一直存在,这也意味着不需要花大量的时间做垃圾回收,而阻塞队列采用链表实现,需要不断的删除、创建节点

Disruptor的核心概念

  • RingBuffer:Disruptor 底层数据结构实现,核心类,是线程间交换数据的中转地

  • Sequence:序号,声明一个序号,用于跟踪 ringbuffer 中任务的变化和消费者的消费情况

  • Sequencer:生产者与缓存 RingBuffer 之间的桥梁,单生产者与多生产者分别对应于两个实现SingleProducerSequencer 与 MultiProducerSequencer,Sequencer 用于向 RingBuffer 申请空间,使用 publish 方法通过 waitStrategy 通知所有在等待可消费事件的 SequenceBarrie

  • SequenceBarrier:序号栅栏,管理和协调生产者的游标序号和各个消费者的序号,确保生产者不会覆盖消费者未来得及处理的消息,确保存在依赖的消费者之间能够按照正确的顺序处理

  • WaitStrategy:有多种实现,用以表示当无可消费事件时,消费者的等待策略

  • Event:消费事件

  • EventProcessor:事件处理器,监听 RingBuffer 的事件,并消费可用事件,从 RingBuffer 读取的事件会交由实际的生产者实现类来消费,它会一直侦听下一个可用的序号,直到该序号对应的事件已经准备好

  • EventHandler:业务处理器,是实际消费者的接口,完成具体的业务逻辑实现,第三方实现该接口,代表着消费者

  • Producer:生产者接口,第三方线程充当该角色,producer 向 RingBuffer 写入事件