Preliminary Disruptor Framework

Getting started with the Disruptor framework

Introduction to Disruptor Concurrency Framework: (Mr. Bai Hexiang)

Martin Fowler在自己网站上写了一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金融交易平台,它能够以很低的延迟产生大量交易。这个
系统是建立在JVM平台上,其核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中,使用事件源驱动方式。
业务逻辑处理器的核心是Disruptor。
Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。
Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。

jar包:

disruptor-3.3.2.jar 
可以通过maven来下载:(坐标)
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.2</version>
</dependency>

Step 1: Create an Event class (this class is the object we consume)

public class LongEvent { 
    private long value;
    public long getValue() { 
        return value; 
    } 

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

Step 2: Create a factory to produce objects

public class LongEventFactory implements EventFactory { 

    @Override 
    public Object newInstance() {   // 用这个工厂批量地产生LongEvent对象
        return new LongEvent(); 
    } 
} 

Step 3: Event consumer, which is an event handler

This event handler simply prints the data stored in the event to the terminal

public class LongEventHandler implements EventHandler<LongEvent>  {
    /**
     * 里面具体写消费的逻辑
     */
    @Override
    public void onEvent(LongEvent longEvent, long l, boolean b) throws Exception {  
        System.out.println(longEvent.getValue());       
    }
}

Step 4: Producer: Implemented by the user

// Producer 由用户实现,它调用RingBuffer来插入事件(Event),在Disruptor中没有相应的实现代码,由用户实现
public class LongEventProducer { 

    private final RingBuffer<LongEvent> ringBuffer;

    public LongEventProducer(RingBuffer<LongEvent> ringBuffer){ // 获得具体存放数据的容器  RingBuffer  是一个环形结构的数据结构
        // 生产者生产数据都应该放在装数据的容器里面
        this.ringBuffer = ringBuffer;
    }
    /**
     * onData用来发布事件,每调用一次就发布一次事件
     * 它的参数会用过事件传递给消费者
     */
    // 生产者生产数据必须遵循这四个步骤
    public void onData(ByteBuffer bb){      // BytyBuffer  是NIO 下面的缓冲  
        //1.可以把ringBuffer看做一个事件队列,那么next就是得到下面一个事件槽
        long sequence = ringBuffer.next();  // 这只是一个下标
        try {
            //2.用上面的索引取出一个空的事件用于填充(获取该序号对应的事件对象)
            LongEvent event = ringBuffer.get(sequence);  // 这个对象里面是没有任何数据的
            //3.获取要通过事件传递的业务数据
            event.setValue(bb.getLong(0));
        } finally { // 一定要写finally
            //4.发布事件
            /*注意,最后的 ringBuffer.publish 方法必须包含在 finally 
             * 中以确保必须得到调用;如果某个请求的 sequence 未被提交,
             * 将会堵塞后续的发布操作或者其它的 producer。
             */
            // 事件监听(消费端会根据槽去获取数据)
            ringBuffer.publish(sequence); // 是把槽发布出去,通过槽可以去找数据
        }
  }
}

Step 5: Test the code:

public class LongEventMain {

public static void main(String[] args) throws Exception {
    //创建缓冲池
    ExecutorService  executor = Executors.newCachedThreadPool();
    //创建工厂
    LongEventFactory factory = new LongEventFactory();
    //创建bufferSize ,也就是RingBuffer大小,必须是2的N次方   否则性能会受到影响
    int ringBufferSize = 1024 * 1024; // 

    /**
    //BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现
    WaitStrategy BLOCKING_WAIT = new BlockingWaitStrategy();
    //SleepingWaitStrategy 的性能表现跟BlockingWaitStrategy差不多,对CPU的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景
    WaitStrategy SLEEPING_WAIT = new SleepingWaitStrategy();
    //YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于CPU逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性
    WaitStrategy YIELDING_WAIT = new YieldingWaitStrategy();
    */

    //创建disruptor

    // 1.第一个参数为工厂类对象,用于创建LongEvent对象,LongEvent是一个个实际的消费的数据
    // 2.第二个参数是缓冲区的大小
    // 3.第三个参数的线程池的大小   进行Disruptor 内部的数据处理和调度
    // 4.第四个参数  : ProducerType.SINGLE(生产者只有一个)  和 ProducerType. MULTI(生产者有多个)
    // 5.第五个参数 :是一种策略   是做协调的
    Disruptor<LongEvent> disruptor = 
            new Disruptor<LongEvent>(factory, ringBufferSize, executor, ProducerType.SINGLE, new YieldingWaitStrategy());
    // 连接消费事件方法   可以把这个Handler理解为消费者  Disruptor把数据推送给Handler进行处理
    disruptor.handleEventsWith(new LongEventHandler());  // 就是一个时间监听的处理器   可以理解成为观察者模式

    // 启动
    disruptor.start();

    //Disruptor 的事件发布过程是一个两阶段提交的过程:
    //发布事件    放数据的地方   
    RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();

    // 这个生产者一般都是自己去实现的
    //LongEventProducer producer = new LongEventProducer(ringBuffer); 
    LongEventProducerWithTranslator producer = new LongEventProducerWithTranslator(ringBuffer);
    ByteBuffer byteBuffer = ByteBuffer.allocate(8);  // 现在可以简单地理解成为字节数组 (缓冲类型的)
    for(long a = 0; a <100; a++){
        byteBuffer.putLong(0, a);
        producer.onData(byteBuffer);
        //Thread.sleep(1000);
    }

    disruptor.shutdown();//关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;
    executor.shutdown();//关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭, disruptor 在 shutdown 时不会自动关闭;      
  }
}

Disruptor Terminology Description (1)

RingBuffer: It is regarded as the main component of Disruptor. However, since 3.0, RingBuffer is only responsible for storing and updating the data circulating in Disruptor. It can be completely replaced by the user (using other data structures) for some special usage scenarios. It can be viewed as simply a container


Sequence: The Disruptor uses Sequence to represent the sequence number handled by a particular component. Like the Disruptor, each consumer (EventProcessor) maintains a Sequence. Most concurrent code relies on the operation of these Sequence values, so Sequence supports a number of features that are currently of the AtomicLong class. It is the slot.
Whether it is a producer or a consumer, the corresponding data is found through the published serial number.


Sequencer: This is the real core of Disruptor. The two producers (single and multi-producer) that implement this interface implement all concurrent algorithms for accurate and fast data transfer between producers and consumers.


SequenceBarrier: Generated by Sequencer and contains references to published Sequences derived from Sequencer and some independent consumer Sequences. It contains the logic to decide whether there is an Event available for consumers to consume.

RringBuffer:

What exactly is a ringbuffer?

答:嗯,正如名字所说的一样,它是一个环(首尾相接的环),你可以把它用做在不同上下文(线程)间传递数据的buffer。

write picture description here

基本来说,ringbuffer拥有一个序号,这个序号指向数组中下一个可用元素。

write picture description here

Some understandings about RingBuffer:

随着你不停地填充这个buffer(可能也会有相应的读取),这个序号会一直增长,直到绕过这个环。

write picture description here

生产者生产数据放在对应的槽位中,假设RingBuffer一共有10个槽位,生产者将生产的LongEvent对象一个一个放在这些槽位中,如果这10个槽位都满了,就会
等待消费者消费,一旦消费了,那么生茶者会继续生产LongEvent对象,放的槽是第11个序号,其实就是转了一圈,还是原来的第一个槽位的位置,只是sequence序号
会不断增加,假设消费端要去消费数据,根据发布的sequence,假设是11.那么11回去根据RringBuffer的槽去取模,从而可以得到对应槽的数据.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325817471&siteId=291194637