Disruptor并发框架学习笔记--01

Disruptor并发框架学习

简介

  • Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题。与Kafka(Apache Kafka)、RabbitMQ(RabbitMQ)用于服务间的消息队列不同,disruptor一般用于线程间消息的传递。基于Disruptor开发的系统单线程能支撑每秒600万订单。
  • disruptor是用于一个JVM中多个线程之间的消息队列,作用与ArrayBlockingQueue有相似之处,但是disruptor从功能、性能都远好于ArrayBlockingQueue,当多个线程之间传递大量数据或对性能要求较高时,可以考虑使用disruptor作为ArrayBlockingQueue的替代者。
  • 核心概念:
    • RingBuffer 环形的缓冲区
      • 曾经 RingBuffer 是 Disruptor 中的最主要的对象,但从3.0版本开始,其职责被简化为仅仅负责对通过 Disruptor 进行交换的数据(事件)进行存储和更新。在一些更高级的应用场景中,Ring Buffer 可以由用户的自定义实现来完全替代。
    • Sequence Disruptor
      • 通过顺序递增的序号来编号管理通过其进行交换的数据(事件),对数据(事件)的处理过程总是沿着序号逐个递增处理。一个 Sequence 用于跟踪标识某个特定的事件处理者( RingBuffer/Consumer )的处理进度。虽然一个 AtomicLong 也可以用于标识进度,但定义 Sequence 来负责该问题还有另一个目的,那就是防止不同的 Sequence 之间的CPU缓存伪共享(Flase Sharing)问题。
    • Sequence
      • 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 没有定义特定接口或类型。
  • 执行简图:
Disruptor并发框架学习笔记--01_第1张图片
Disruptor并发框架学习笔记--01_第2张图片
  • 使用步骤:
    • 创建Event类对象(数据对象)

      • 事件(Event)就是通过 Disruptor 进行交换的数据类型。
      •   public class MessageEvent {
          	private AsyncEvent asyncEvent;
              //TODO Getter/Setter 方法
          }
        
    • 建立一个生产数据的工厂类,EventFactory,用于生产数据

      • 事件工厂(Event Factory)定义了如何实例化前面第1步中定义的事件(Event),需要实现接口 com.lmax.disruptor.EventFactory。
      • Disruptor 通过 EventFactory 在 RingBuffer 中预创建 Event 的实例。
      • 一个 Event 实例实际上被用作一个“数据槽”,发布者发布前,先从 RingBuffer 获得一个 Event 的实例,然后往 Event 实例中填充数据,之后再发布到 RingBuffer 中,之后由 Consumer 获得该 Event 实例并从中读取数据。
      •   public class MessageEventFactory implements EventFactory {
          	@Override
          	public MessageEvent newInstance() {
          		return new MessageEvent();
          	}
          }
        
    • 定义事件处理的具体实现

      • 通过实现接口 com.lmax.disruptor.EventHandler 定义事件处理的具体实现。
      •   public abstract class MessageEventHandler implements EventHandler{
              public void onEvent(MessageEvent event, long sequence, boolean endOfBatch){
                  this.handleEvent(event.getAsyncEvent().toString());
              }
              public abstract void handleEvent(String event) throws Exception;
          }
        
    • 定义用于事件处理的线程池

      • Disruptor 通过 java.util.concurrent.ExecutorService 提供的线程来触发 Consumer 的事件处理。
      •   ExecutorService executor = Executors.newCachedThreadPool();
        
    • 指定等待策略

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

      •   EventFactory eventFactory = new MessageEventFactory();
          ExecutorService executor = Executors.newSingleThreadExecutor();
          int ringBufferSize = 1024 * 1024; // RingBuffer 大小,必须是 2 的 N 次方;
        
          Disruptor disruptor = new Disruptor(eventFactory,
                          ringBufferSize, executor, ProducerType.SINGLE,
                          new YieldingWaitStrategy());
        
          EventHandler eventHandler = new MessageEventHandler();
          disruptor.handleEventsWith(eventHandler);
        
          disruptor.start();
        
        
    • 发布事件:

      • Disruptor 的事件发布过程是一个两阶段提交的过程:
        • 第一步:先从 RingBuffer 获取下一个可以写入的事件的序号;
        • 第二步:获取对应的事件对象,将数据写入事件对象;
        • 第三部:将事件提交到 RingBuffer;
      • 事件只有在提交之后才会通知 EventProcessor 进行处理;
      •   public class MessageEventProducer {
        
          	private final RingBuffer ringBuffer;
        
          	public MessageEventProducer(RingBuffer ringBuffer) {
          		this.ringBuffer = ringBuffer;
          	}
        
          	public void onData(final AsyncEvent asyncEvent) {
          		long sequence = ringBuffer.next();  // Grab the next sequence
          		try {
          			MessageEvent event = ringBuffer.get(sequence); // Get the entry in the Disruptor
          			event.setAsyncEvent(asyncEvent);
          		} finally {
          		    //注:ringBuffer.publish 方法必须包含在 finally 中以确保必须得到调用;如果某个请求的 sequence 未被提交,将会堵塞后续的发布操作或者其它的 producer。
          			ringBuffer.publish(sequence);
          		}
          	}
          }
        
    • 关闭 Disruptor:

      • 优雅的关闭:
      •   Runtime.getRuntime().addShutdownHook(new ShutdownHandler());
        
          private class ShutdownHandler extends Thread {
          		@Override
          		public void run() {
          			if (disruptor != null) {
          				disruptor.shutdown();
          			}
          			disconnect();
          		}
          	}
          //executor 关闭
          private void disconnect() {
          		if (executor != null && executor.isConnected()) {
          			try {
          				executor.shutdown();//关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭, disruptor 在 shutdown 时不会自动关闭;
          			} catch (Exception e) {
          				logger.error("executor Client disconnect failed ! ", e);
          			}
          		}
          	}
        

你可能感兴趣的:(并发编程)