Disruptor并发框架使用一

一、Disruptor并发框架简介
   能够以很低的延迟,产生大量的交易,建立在JVM平台上,核心是一个业务逻辑的处理器,它能够在一个线程里每秒处理六百万订单。业务逻辑处理器是完全运行在内存中,使用事件源驱动方式。能够在无锁的情况下,实现网络Queue并发操作。
   Disruptor是一个高性能的异步处理框架,或者认为是最快的消息处理框架(轻量JMS java message serverice),也可以认为是以一个观察者的模式实现,也可以任务是以一个监听者的模式实现。你可以理解为他是一种高效的"生产者-消费者"模型。也就性能远远高于传统的BlockingQueue容器。
   官网学习:点击此处
二、创建HelloWorld基本步骤
   第一:建立一个Event类
   第二:建立一个工厂Event类,用于创建Event类实例对象
   第三:需要有一个监听事件类,用于处理数据(Event类)
   第四:我们需要进行测试代码编写。实例化Disruptor实例,配置一系列参数。然后我们对Disruptor实例绑定监听事件类,接受并处理数据。
   第五:在Disruptor中,真正存储数据的核心叫做RingBuffer,我们通过Disruptor实例拿到它,然后把数据生产出来,把数据加入到RingBuffer的实例对象中即可。
三、Disruptor相关介绍
3.1 disrutor 术语说明
   1、RingBuffer:被看做Disruptor最主要的组件。从3.0开始,RingBuffer仅仅作为存储和更新Disrutor中流通的数据。对一些特殊使用场景能够(使用其他数据结构)完全代替。
   2、Sequence:Disrutor使用Sequence来表示一个特殊组件处理的序号。和Disrutor一样,每个消费者(EventProcessor)都维持着一个Sequence。大部分的并发代码依赖着这些Sequence值的运转,因此Sequence支持多种当前为AutomicLong类的特性。
   3、Sequencer:这个是Disruptor的核心。实现了这个接口的两种生产者(单生产者和多生产者)均实现了所有的并发算法,为了在生产者和消费者之间进行准确快速的数据传递。
   4、SequenceBarrier:由Sequencer组成,并且包含了已经发布了Sequence引用,这些sequence源于Sequencer和一些独立的消费者的sequence,它包含了决定是否有供消费者来消费的Event的逻辑
   5、WaitStrategy:决定一个消费者将如何等待生产者将Event置入Disruptor
   6、Event:从生产者到消费者过程中所处理的数据单元。此完全由用户来定义
   7、EventProcessor: 主要事件循环,处理Disruptor中的Event,并且拥有消费者的Sequence。它有一个实现类是BatchEventProcessor,包含了EventLoop的有效实现,并且将回调到一个EventHandler接口的实现对象
   8、EventHandler:由用户实现并且代表了Disruptor中的一个消费者的接口
   9、Producer:由用户实现,它调用RingBuffer来插入时间Event
   10、WorkProcessor:确保每个sequence只被一个processor消费,在同一个workPool中的处理多个WorkProcessor不会消费同样的Sequence
   11、WorkPool:一个WorkProcessor池,其中WorkProcessor将消费sequence,所以任务可以在实现WorkHandler接口的work之间移交
   12、LifecycleAware:当BatchEventProcessor启动和停止时,于实现这个接口用于接收通知。
在这里插入图片描述
3.2 RingBuffer
   1、ringbuffer是不同上线文(线程)间传递数据的buffer。基本上来说,ringbuffer拥有一个序列号,这个序号指向数组中下一个可用元素
在这里插入图片描述在这里插入图片描述
   2、随着你不停的填充这个buffer(可能也会有响应的读取),这个序号会一直增长,直到绕过这个环
在这里插入图片描述
   3、要找到数组中当前序号指向的元素,可以通过mod操作,sequence mod array length = array index(取模操作)以上面的ringbuffer为例(java的mod语法):12 % 10 = 2 。ringbuffer是没有尾指针,我们只是维护了一个指向下一个的可用位置的序号。ringbuffer与队列最大的区别是:不删除buffer中的数据,也就说这些数据一直存在buffer中,直到新的数据覆盖他们
   4、因为ringbuffer是数组,所以要比链表快,而且有一个容易预测的数据模式
   5、这是对CPU缓存友好的,也就是说在硬件级别,数组中的元素是会被预加载的,因此在ringbuffer当中,cpu无需时不时去主存加载数组中的下一个元素。你可以为数组预先分配内存,使得数组对象一直存在(除非程序终止),这就意味着不需要花大量的时间进行垃圾回收,此外,不像链表那样,需要为每一个添加到其上面的对象创造节点对象一样,当删除节点时,需要执行相应的内存清理操作

/**
 * @ClassName: LongEvent
 * @Description:
 * @Author: Administrator
 * @Date: 2020-07-16 00:10
 * @Version:1.0
 */
public class LongEvent{

    private  Long value;

    public Long getValue() {
        return value;
    }

    public void setValue(Long value) {
        this.value = value;
    }
}
/**
 * @ClassName: LongEventFactory
 * @Description: 需要disruptor为我们创建对象,同时还声明了EventFactory
 *              来实例化Event对象
 * @Author: Administrator
 * @Date: 2020-07-16 00:11
 * @Version:1.0
 */
public class LongEventFactory implements EventFactory {
    @Override
    public Object newInstance() {
        return new LongEvent();
    }
}
/**
 * @ClassName: LongEventHandler
 * @Description: 事件消费者,也就是事件处理器,此例中的事件处理器
 *                简单的把事件中存储的数据打印到终端
 * @Author: Administrator
 * @Date: 2020-07-19 16:38
 * @Version:1.0
 */
public  class LongEventHandler implements EventHandler<LongEvent>  {

    @Override
    public void handle(LongEvent event) {
        System.out.println(event.getValue());
    }
}

/**
 * @ClassName: LongEventProducer
 * @Description: 当一个简单队列来发布事件的时候,会牵涉到更多的细节,这是因为事件对象还需要预先创建
 *               发布事件最少需要两步:1.获取下一个时间槽并发布事件(发布事件的时候要用try/finally保证事件一定会被发布)
 *               如果我们用RingBuffer.next()获取一个事件槽,那么一定要发布对应的事件
 *               如果不能发布事件,就会造成Disruptor状态的混乱
 *               尤其是在多个事件生产者的情况下,会导致事件消费者失速,从而不得不重启应用才能恢复
 * @Author: Administrator
 * @Date: 2020-07-19 18:27
 * @Version:1.0
 */
public class LongEventProducer {
    
    private final RingBuffer<LongEvent> ringBuffer;

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

    /**
     * onData 用来发布事件,每调用一次,就发布一次事件
     *        它的参数会用事件传递给消费者
     *
     * @param bb
     */
    public void onData(ByteBuffer bb){
        // 1.可以把RingBuffer看做一个事件队列,那么next就是得到下面一个事件槽
        long sequence = ringBuffer.next();
        try {
            // 2.用上面的一个索引取出一个空的事件用于填充(获取该序号对应的事件对象)
            LongEvent event = ringBuffer.get(sequence);
            // 3.获取要通过事件传递的业务数据
            event.setValue(bb.getLong(0) );
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            // 4.发布事件
            //注意:最后的ringBuffer.publish 方法必须包含在finally中以确保必须得到调用,
            //如果某个请求的sequence未被提交,将会堵塞后续的发布操作或者其他的producer
            ringBuffer.publish(sequence);


        }
    }
    
}

/**
 * @ClassName: LongEventProducerWithTranslator
 * @Description: Disruptor 3.0 之后提供了lamda式的API,这样可以把复杂的操作放在RingBuffer中
 *              在3.0 以后的版本中,最好用Event Publiser或者Event Translator来发布事件
 * @Author: Administrator
 * @Date: 2020-07-19 19:13
 * @Version:1.0
 */
public class LongEventProducerWithTranslator {

    private RingBuffer<LongEvent> ringBuffer;

    public static final EventTranslatorOneArg<LongEvent,ByteBuffer> TRANSLATOR = new EventTranslatorOneArg<LongEvent, ByteBuffer>() {
        @Override
        public void translateTo(LongEvent event, long l, ByteBuffer byteBuffer) {
            event.setValue(byteBuffer.getLong(0));
        }
    };
    
    public RingBuffer<LongEvent> getRingBuffer() {
        return ringBuffer;
    }

    public void setRingBuffer(RingBuffer<LongEvent> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }
}
public class LongEventMain {
    public static void main(String[] args) {
        // 1 创景缓冲池
        ExecutorService executor =  Executors.newCachedThreadPool();
        // 2 创建工厂
        LongEventFactory eventFactory = new LongEventFactory();
        // 3 创建bufferSize,也就是RingBuffer的大小,必须为2的n次方
        int ringBufferSize = 1024 * 1024;
        // 4 创建disruptor
        /**
         * WaitStrategy
         * 1、BlockingWaitStrategy 最低效的策略,但其对cpu的消耗最小,并且能够在各种不同的部署环境中
         *    能提供更加一致的性能表现 WaitStrategy BLOCKING_WAIT = new BlockingWaitStrategy();
         * 2、SleepingWaitStrategy 性能表现根BlockingWaitStrategy差不多,对CPU的消耗也类似,但其对
         *    生产者线程的影响最小,适合用于异步日志类似的场景
         *    WaitStrategy BLOCKING_WAIT = new SleepingWaitStrategy();
         * 3、YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线程数
         *    小于CPU逻辑核心数的场景中,推荐使用此策略,
         *
         */
        Disruptor<LongEvent> disruptor = new Disruptor<LongEvent>(eventFactory,ringBufferSize,executor, ProducerType.SINGLE, new YieldingWaitStrategy());
        // 5 连接消费事件方法
        disruptor.handleEventsWith((EventHandler<? super LongEvent>) new LongEventHandler());
        // 6 启动
        disruptor.start();
        // 7 Disruptor 事件发布是一个两阶段提交的过程
        // 发布事件
        RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
        LongEventProducer producer = new LongEventProducer(ringBuffer);
        //LongEventProducerWithTranslator producer = new LongEventProducerWithTranslator(ringBuffer);
        ByteBuffer byteBuffer = ByteBuffer.allocate(8);
        for (int i = 0; i < 1000; i++) {
           byteBuffer.putLong(0,i);
           producer.onData(byteBuffer);
            //Thread.sleep(1000);
        }

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


    }


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值