Disruptor编程指南

来源:互联网 发布:sublime codeintel php 编辑:程序博客网 时间:2024/06/05 15:46

Disruptor编程指南

@(STORM)[storm, java]

完整代码请见:https://github.com/lujinhong/lujinhong-commons/tree/master/lujinhong-commons-java/src/main/java/com/lujinhong/commons/java/disruptor
部分内容参考自:http://www.cnblogs.com/haiq/p/4112689.html

  • Disruptor编程指南
    • 一什么是 Disruptor
    • 二Disruptor 的核心概念
    • 三编程指南
      • 1创建事件
      • 2创建事件工厂
      • 3创建事件处理器
      • 4主类
    • 四其它内容
      • 1等待策略
      • 2关于消息发布

一、什么是 Disruptor

从功能上来看,Disruptor 是实现了“队列”的功能,而且是一个有界队列。那么它的应用场景自然就是“生产者-消费者”模型的应用场合了。

可以拿 JDK 的 BlockingQueue 做一个简单对比,以便更好地认识 Disruptor 是什么。

我们知道 BlockingQueue 是一个 FIFO 队列,生产者(Producer)往队列里发布(publish)一项事件(或称之为“消息”也可以)时,消费者(Consumer)能获得通知;如果没有事件时,消费者被堵塞,直到生产者发布了新的事件。

这些都是 Disruptor 能做到的,与之不同的是,Disruptor 能做更多:

同一个“事件”可以有多个消费者,消费者之间既可以并行处理,也可以相互依赖形成处理的先后次序(形成一个依赖图);预分配用于存储事件内容的内存空间;针对极高的性能目标而实现的极度优化和无锁的设计;

以上的描述虽然简单地指出了 Disruptor 是什么,但对于它“能做什么”还不是那么直截了当。一般性地来说,当你需要在两个独立的处理过程(两个线程)之间交换数据时,就可以使用 Disruptor 。当然使用队列(如上面提到的 BlockingQueue)也可以,只不过 Disruptor 做得更好。

拿队列来作比较的做法弱化了对 Disruptor 有多强大的认识,如果想要对此有更多的了解,可以仔细看看 Disruptor 在其东家 LMAX 交易平台(也是实现者) 是如何作为核心架构来使用的,这方面就不做详述了,问度娘或谷哥都能找到。

二、Disruptor 的核心概念

先从了解 Disruptor 的核心概念开始,来了解它是如何运作的。下面介绍的概念模型,既是领域对象,也是映射到代码实现上的核心对象。

Ring Buffer
如其名,环形的缓冲区。曾经 RingBuffer 是 Disruptor 中的最主要的对象,但从3.0版本开始,其职责被简化为仅仅负责对通过 Disruptor 进行交换的数据(事件)进行存储和更新。在一些更高级的应用场景中,Ring Buffer 可以由用户的自定义实现来完全替代。
Sequence Disruptor
通过顺序递增的序号来编号管理通过其进行交换的数据(事件),对数据(事件)的处理过程总是沿着序号逐个递增处理。一个 Sequence 用于跟踪标识某个特定的事件处理者( RingBuffer/Consumer )的处理进度。虽然一个 AtomicLong 也可以用于标识进度,但定义 Sequence 来负责该问题还有另一个目的,那就是防止不同的 Sequence 之间的CPU缓存伪共享(Flase Sharing)问题。
(注:这是 Disruptor 实现高性能的关键点之一,网上关于伪共享问题的介绍已经汗牛充栋,在此不再赘述)。
Sequencer
Sequencer 是 Disruptor 的真正核心。此接口有两个实现类 SingleProducerSequencer、MultiProducerSequencer ,它们定义在生产者和消费者之间快速、正确地传递数据的并发算法。
Sequence Barrier
用于保持对RingBuffer的 main published Sequence 和Consumer依赖的其它Consumer的 Sequence 的引用。 Sequence Barrier 还定义了决定 Consumer 是否还有可处理的事件的逻辑。
Wait Strategy
定义 Consumer 如何进行等待下一个事件的策略。 (注:Disruptor 定义了多种不同的策略,针对不同的场景,提供了不一样的性能表现)
Event
在 Disruptor 的语义中,生产者和消费者之间进行交换的数据被称为事件(Event)。它不是一个被 Disruptor 定义的特定类型,而是由 Disruptor 的使用者定义并指定。
EventProcessor
EventProcessor 持有特定消费者(Consumer)的 Sequence,并提供用于调用事件处理实现的事件循环(Event Loop)。
EventHandler
Disruptor 定义的事件处理接口,由用户实现,用于处理事件,是 Consumer 的真正实现。
Producer
即生产者,只是泛指调用 Disruptor 发布事件的用户代码,Disruptor 没有定义特定接口或类型。

image

三、编程指南

1、创建事件

在disruptor中,事件即是我们常说的消息。

public class LongEvent {    private long _value;    public void set(long value){        _value = value;    }    public long getValue(){        return _value;    }   }

2、创建事件工厂

public class LongEventFactory implements EventFactory<LongEvent>{    @Override    public LongEvent newInstance() {        return new LongEvent();    }}

3、创建事件处理器

public class LongEventHandler implements EventHandler<LongEvent> {    @Override    public void onEvent(LongEvent event, long sequent, boolean endOfBatch) throws Exception {        System.out.println("Dealing " + event.getValue());    }}

4、主类

主类只要写成2件事:
(1)启动disruptor,并指定处理事件的handler
(2)向Disruptor中发布事件

public class DisruptorDemo {    private static Random random = new Random();    public static void main(String[] args) {        // 一、启动disruptor,并指定处理事件的handler        EventFactory<LongEvent> eventFactory = new LongEventFactory();        ExecutorService executor = Executors.newSingleThreadExecutor();        int ringBufferSize = 1024 * 1024; // RingBuffer 大小,必须是 2 的 N 次方;        Disruptor<LongEvent> disruptor = new Disruptor<LongEvent>(eventFactory, ringBufferSize, executor,                ProducerType.SINGLE, new YieldingWaitStrategy());        //指定使用哪个handler来处理事件        EventHandler<LongEvent> eventHandler = new LongEventHandler();        disruptor.handleEventsWith(eventHandler);        disruptor.start();        // 二、向Disruptor中发布事件        RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();        for (int i = 0; i < 100; i++) {            long sequence = ringBuffer.next();// 请求下一个事件序号;            try {                LongEvent event = ringBuffer.get(sequence);// 获取该序号对应的事件对象;                long data = getEventData();// 获取要通过事件传递的业务数据;                event.set(data);            } finally {                ringBuffer.publish(sequence);// 发布事件;            }        }        disruptor.shutdown();// 关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;        executor.shutdown();// 关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭, disruptor 在                            // shutdown 时不会自动关闭;    }    private static long getEventData() {        return random.nextLong();    }}

四、其它内容

1、等待策略

Disruptor 定义了 com.lmax.disruptor.WaitStrategy 接口用于抽象 Consumer 如何等待新事件,这是策略模式的应用。
Disruptor 提供了多个 WaitStrategy 的实现,每种策略都具有不同性能和优缺点,根据实际运行环境的 CPU 的硬件特点选择恰当的策略,并配合特定的 JVM 的配置参数,能够实现不同的性能提升。
例如,BlockingWaitStrategy、SleepingWaitStrategy、YieldingWaitStrategy 等,其中,
BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现;
SleepingWaitStrategy 的性能表现跟 BlockingWaitStrategy 差不多,对 CPU 的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景;
YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于 CPU 逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。

2、关于消息发布

此外,Disruptor 要求 RingBuffer.publish 必须得到调用的潜台词就是,如果发生异常也一样要调用 publish ,那么,很显然这个时候需要调用者在事件处理的实现上来判断事件携带的数据是否是正确的或者完整的,这是实现者应该要注意的事情。

此外,Disruptor 要求 RingBuffer.publish 必须得到调用的潜台词就是,如果发生异常也一样要调用 publish ,那么,很显然这个时候需要调用者在事件处理的实现上来判断事件携带的数据是否是正确的或者完整的,这是实现者应该要注意的事情。

Disruptor 还提供另外一种形式的调用来简化以上操作,并确保 publish 总是得到调用。

static class Translator implements EventTranslatorOneArg<LongEvent, Long>{    @Override    public void translateTo(LongEvent event, long sequence, Long data) {        event.set(data);    }    }public static Translator TRANSLATOR = new Translator();public static void publishEvent2(Disruptor<LongEvent> disruptor) {    // 发布事件;    RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();    long data = getEventData();//获取要通过事件传递的业务数据;    ringBuffer.publishEvent(TRANSLATOR, data);}
原创粉丝点击