Disruptor并发框架的理解

1、什么是Disruptor?
  Disruptor 是实现了“队列”的功能,而且是一个有界队列。那么它的应用场景自然就是“生产者-消费者”模型的应用场合了。
  可以拿 JDK 的 BlockingQueue 做一个简单对比,以便更好地认识 Disruptor 是什么。

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

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

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


  自己总结:Disruptor实现了‘队列’的功能,是‘生产者-消费者’模型的应用场合
          它不仅仅能够实现生产者--->数据<---消费者,也能够实现同一'事件'可以有多个消费者并行处理,也可以相互依赖形成处理的先后顺序




2、Disruptor的核心概念:
  Ring Buffer:环形缓冲区,他是负责对通过Disruptor进行交换数据(事件)进行存储和更新
  SequenceDisruptor:通过顺序递增的序号来编号,一个Sequence用于跟踪标识别某个特定的事件处理者,还有个作用是:防止不同的Sequence之间的CPU缓存伪共享的问题(注:这是Disruptor实现高性能的关键点)
  Sequencer:用来定义生产者和消费者之间快速、正确的传递数据的并发算法
  Sequence Barrier:用于保持对RingBuffer的 main published Sequence 和Consumer依赖的其它Consumer的 Sequence 的引用。 Sequence Barrier 还定义了决定 Consumer 是否还有可处理的事件的逻辑。
  wait Strategy:定义Consumer如何进行等待下个时间的策略
  Event:生产者和消费者之间进行交换的数据被称为时间(自定义的)
  EventProcessor:持有特定消费者(consumer)的sequence,并提供调用事件处理实现的事件循环
  EventHandler:Disrutor定义的时间处理接口(用户自己实现)
  Producer:生产者


  整体的运行流程图在:https://www.cnblogs.com/haiq/p/4112689.html这个博客上


3、如何使用Disruptor:
  3.1:定义时间
  事件就是通过Disruptor进行交换的数据类型
      public class LongEvent{
        private long value;

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


  3.2定义事件工厂
    Disruptor通过EventFactory在RingBuffer中预创建Event的实例
    public class LongEventFactory implements EventFactory<LongEvent>{
      public LongEvent new Instance(){
        return new LongEvent();
      }
    }


  3.3定义事件处理的具体实现
  public class LongEventHandler implements EventHandler<LongEvent>{
    public void onEvent(LongEvent event, long sequence, boolean endOfBatch){
      System.out.println("Event:"+event):
    }
  }


  3.4定义用于事件处理的线程池
  Disruptor通过java.util.concurrent.ExecutorService提供的线程来触发Consumer的事件处理。例如:
    ExecutorService executor = Executors.newCachedThreadPool();


  3.5指定等待策略
    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();


  3.6启动 Disruptor
    //生成数据的
    EventFactory<LongEvent> eventFactory = new LongEventFactory();
    //创建缓存池的
    ExecutorService executor =Excutors.newSingleThreadExecutor();
    int ringBufferSize = 1024*1024;

    //创建disruptor
    Disruptor<LongEvent> disruptor = new Disruptor<LognEvent)(eventFactory,ringBufferSize,executor,ProducerType.SINGLE,new YieldingWaitStrategy());

    EventHandler<LongEvent> eventHandler = new LongEventHandler();
    disruptor.handleEventsWith(eventHandler);

    disruptor.start();

  3.7发布事件
    Disruptor的事件发布过程是一个两阶段提交的过程
      第一步:先从RingBuffer获取下一个可以写入的事件序号;
      第二步:获取对应的事件内容,将数据写入事件对象
      第三步:将时间提交到RingBuffer

      RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
      long sequence = ringBuffer.next();//请求的下一个事件序号

      trt{
        LongEvent event = ringBuffer.get(sequence);//获取改序号对应的事件对象
        long data = getEventData();//从RingBuffer中获取到数据内容
        event.set(data);//封装成实体

      }finally{
        ringBuffer.publish(sequence);//发布事件
      }


  3.8关闭 Disruptor
   disruptor.shutdown();//关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;
   executor.shutdown();//关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭, disruptor 在 shutdown 时不会自动关闭;
发布了45 篇原创文章 · 获赞 28 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_15006743/article/details/84633705
今日推荐