Disruptor 极速体验

来源:互联网 发布:水冷机箱推荐 知乎 编辑:程序博客网 时间:2024/05/15 08:43

LMAX是一种新型零售金融交易平台,它能够以很低的延迟(latency)产生大量交易(吞吐量). 这个系统是建立在JVM平台上,核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单. 业务逻辑处理器完全是运行在内存中(in-memory),使用事件源驱动方式(event sourcing). 业务逻辑处理器的核心是Disruptors,这是一个并发组件,能够在无锁的情况下实现网络的Queue并发操作。他们的研究表明,现在的所谓高性能研究方向似乎和现代CPU设计是相左的.

一、什么是 Disruptor

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

一般性地来说,当你需要在两个独立的处理过程(两个线程)之间交换数据时,就可以使用 Disruptor 。当然使用队列(BlockingQueue)也可以,只不过 Disruptor 做得更好。

二、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 的使用者定义并指定。EventProcessorEventProcessor 持有特定消费者(Consumer)的 Sequence,并提供用于调用事件处理实现的事件循环(Event Loop)。EventHandlerDisruptor 定义的事件处理接口,由用户实现,用于处理事件,是 Consumer 的真正实现。Producer即生产者,只是泛指调用 Disruptor 发布事件的用户代码,Disruptor 没有定义特定接口或类型。

三、如何使用 Disruptor

Disruptor 的 API 十分简单,主要有以下几个步骤:

定义事件事件(Event)就是通过 Disruptor 进行交换的数据类型。
 package com.disruptor2;import com.lmax.disruptor.EventFactory;/** * 消费事件 */public class PersonEvent {    private Person person;    public Person getPerson() {        return person;    }    public void setPerson(Person person) {        this.person = person;    }/* 定义事件工厂*/    public final static  EventFactory<PersonEvent> EVENT_FACTORY = new EventFactory<PersonEvent>(){        public PersonEvent newInstance(){            return new PersonEvent();        }    };}
定义事件处理的具体实现通过实现接口 com.lmax.disruptor.EventHandler<T> 定义事件处理的具体实现。
package com.disruptor2;import java.util.Date;import com.lmax.disruptor.EventHandler;/** * 消费事件处理处理器 */public class PersonEventHandler  implements EventHandler<PersonEvent>{    public PersonEventHandler(){    }    @Override    public void onEvent(PersonEvent event, long sequence, boolean endOfBatch)            throws Exception {        Person person = event.getPerson();        System.out.println("第 "+sequence+" 个消费结束:"+endOfBatch+"   "+new Date().toLocaleString());    }}
定义用于事件处理的线程池Disruptor 通过 java.util.concurrent.ExecutorService 提供的线程来触发 Consumer 的事件处理。例如:ExecutorService executor = Executors.newCachedThreadPool();指定等待策略Disruptor 定义了 com.lmax.disruptor.WaitStrategy 接口用于抽象 Consumer 如何等待新事件,这是策略模式的应用。Disruptor 提供了多个 WaitStrategy 的实现,每种策略都具有不同性能和优缺点,根据实际运行环境的 CPU 的硬件特点选择恰当的策略,并配合特定的 JVM 的配置参数,能够实现不同的性能提升。例如,BlockingWaitStrategy、SleepingWaitStrategy、YieldingWaitStrategy 等,其中,BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现;SleepingWaitStrategy 的性能表现跟 BlockingWaitStrategy 差不多,对 CPU 的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景;YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于 CPU 逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。WaitStrategy BLOCKING_WAIT = new BlockingWaitStrategy();WaitStrategy SLEEPING_WAIT = new SleepingWaitStrategy();WaitStrategy YIELDING_WAIT = new YieldingWaitStrategy();
package com.disruptor2;import java.util.Date;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import com.lmax.disruptor.BatchEventProcessor;import com.lmax.disruptor.EventHandler;import com.lmax.disruptor.RingBuffer;import com.lmax.disruptor.SequenceBarrier;import com.lmax.disruptor.YieldingWaitStrategy;import com.lmax.disruptor.dsl.Disruptor;import com.lmax.disruptor.dsl.ProducerType;public class PersonHelper {    private  static PersonHelper instance;    /**     * ringBuffer的容量,必须是2的N次方     */    private static final int BUFFER_SIZE = 2048;    //用于处理数据结构跟踪为出版商和相关eventprocessors序列光标协调障碍    private Disruptor<PersonEvent> disruptor;    public PersonHelper(){        ExecutorService executor = Executors.newCachedThreadPool();        disruptor = new Disruptor<PersonEvent>(PersonEvent.EVENT_FACTORY,                BUFFER_SIZE, executor, ProducerType.SINGLE,                new YieldingWaitStrategy());        EventHandler<PersonEvent> eventHandler = new PersonEventHandler();        disruptor.handleEventsWith(eventHandler);        disruptor.start();    }    /**     * 启动消费者线程,实际上调用了AudioDataEventHandler中的onEvent方法进行处理     */    public static void start(){        instance = new PersonHelper();    }    /**     * 停止     */    public static void shutdown(){        instance.doHalt();    }    private void doHalt() {        disruptor.halt();    }    /**     * 生产者生产商品     * @param person     */    private void doProduce(Person person){        RingBuffer<PersonEvent> ringBuffer=disruptor.getRingBuffer();        //获取下一个序号        long sequence = ringBuffer.next();        //写入数据        disruptor.get(sequence).setPerson(person);        //通知消费者该资源可以消费了        System.out.println("第  "+sequence+"  个生产完了  "+"开始消费:"+new Date().toLocaleString());        ringBuffer.publish(sequence);    }    /**     * 生产者压入生产数据     * @param data     */    public static void produce(Person person){        instance.doProduce(person);    }}
发布事件Disruptor 的事件发布过程是一个两阶段提交的过程:  第一步:先从 RingBuffer 获取下一个可以写入的事件的序号;  第二步:获取对应的事件对象,将数据写入事件对象;  第三部:将事件提交到 RingBuffer;
测试类:package com.disruptor2;public class Test {    /**     * @param args     */    public static void main(String[] args) {        PersonHelper.start();        for(int i=0 ; i<200; i++){            Person p = new Person("zs"+i, i , "男", "1234566"+i);            //生产者生产数据            PersonHelper.produce(p);        }        //PersonHelper.shutdown();    }}
0 0
原创粉丝点击