Disruptor的使用

最近做一个项目需要异步写日志,用到Disruptor。本文做个简单Disruptor示例说明Disruptor如何使用。

一、示例

本例共包含7个类:

测试程序类型:App.java
disruptor工具类:DisruptorUtil.java
消息实体类:MessageEvent.java
消息消费者类:MessageEventConsumer.java
消息事件工厂类:MessageEventFactory.java
消息生产者类:MessageEventProducer.java
消费者线程工厂:EventThreadFactory.java
1、定义消息事件实体类 MessageEvent.java,这是个简单类,用于装载消息,本例只定义了一个String 类型的成员,可根据需要定义其它类型的成员
/**
* 消息的装载对象
*/
public class MessageEvent {

  private String message;

  public String getMessage() {
    return this.message;
  }

  public void setMessage(String message) {
    this.message = message;
  }
}
2、定义消息事件工厂类:MessageEventFactory.java,用于disruptor生产1中定义的消息事件实体类
/**
* 事件工厂,disruptor生产消息对象
*
* @author yang.kuowei
* @version 1.0
* @date 2019-05-14 19:20
*/
public class MessageEventFactory implements EventFactory<MessageEvent> {
  @Override
  public MessageEvent newInstance() {
    return new MessageEvent();
  }
}
3、定义消息生产者类:MessageEventProducer.java,用于向disruptor发布消息
/**
* 生产者,在此类中向disruptor发布消息
*
* @author yang.kuowei
* @version 1.0
* @date 2019-05-14 19:25
*/
public class MessageEventProducer {
  private RingBuffer<MessageEvent> ringBuffer;

  public MessageEventProducer(RingBuffer<MessageEvent> ringBuffer) {
    this.ringBuffer = ringBuffer;
  }
  /**
   * 将接收到的消息输出到ringBuffer
   */
  public void onData(String messageStr) {
    //请求下一个事件序号;
    long sequence = ringBuffer.next();
    try {
      //获取该序号对应的事件对象;
      MessageEvent event = ringBuffer.get(sequence);
      event.setMessage(messageStr);
      System.out.println("生产者发布消息:" + messageStr);
    }
    finally {
      //发布事件;
      ringBuffer.publish(sequence);
    }
  }
}
4、消息消费者类:MessageEventConsumer.java,实现具体的业务逻辑,本例只是打印传来的字符串
/**
* 消费者,在此类实现具体的业务逻辑
*
* @author yang.kuowei
* @version 1.0
* @date 2019-05-14 19:23
*/
public class MessageEventConsumer implements EventHandler<MessageEvent> {
  @Override
  public void onEvent(MessageEvent event, long sequence, boolean endOfBatch) throws Exception {
    System.out.println("消费者消费消息:" + event.getMessage());
  }
}
5、消费者线程工厂:EventThreadFactory.java,用于生产消费者线程,在构造Disruptor的适合传入这个类的一个实例
/**
* 线程工厂,用于生产消费者线程
*
* @author yang.kuowei
* @version 1.0
* @date 2019-05-14 20:13
*/
public class EventThreadFactory implements ThreadFactory {
  @Override
  public Thread newThread(Runnable r) {
    return new Thread(r);
  }
}
6、disruptor工具类:DisruptorUtil.java,用于实现disruptor的构造,启动,关闭,发布消息
/**
* disrupt工具类
*
* @author yang.kuowei
* @version 1.0
* @date 2019-05-14 19:34
*/
public final class DisruptorUtil {

  private static DisruptorUtil disruptorUtil;

  private Disruptor<MessageEvent> disruptor;

  // RingBuffer 大小,必须是 2 的 N 次方;
  private static final int RING_BUFFER_SIZE = 1024 * 1024;

  private RingBuffer<MessageEvent> ringBuffer;

  private MessageEventProducer messagemEventProducer;

  private final MessageEventConsumer messageEventConsumer;

  private DisruptorUtil() {
    EventThreadFactory eventThreadFactory = new EventThreadFactory();
    EventFactory<MessageEvent> eventFactory = new MessageEventFactory();
    disruptor = new Disruptor<MessageEvent>(eventFactory, RING_BUFFER_SIZE, eventThreadFactory, ProducerType.SINGLE,
      new YieldingWaitStrategy());
    messageEventConsumer = new MessageEventConsumer();
    disruptor.handleEventsWith(messageEventConsumer);
  }

  /**
   * 获取 LogDisruptorUtil 实例
   *
   * @return LogDisruptorUtil
   */
  public static DisruptorUtil getInstance() {
    if (disruptorUtil == null) {
      synchronized (LogDisruptorUtil.class) {
        if (disruptorUtil == null) {
          disruptorUtil = new DisruptorUtil();
          return disruptorUtil;
        }
      }
    }
    return disruptorUtil;
  }
  /**
   * 启动disruptor
   */
  public void start() {
    disruptor.start();
    ringBuffer = disruptor.getRingBuffer();
    messagemEventProducer = new MessageEventProducer(ringBuffer);
    //应用关闭前关闭disrupt
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
      @Override
      public void run() {
        disruptor.shutdown();
      }
    }));
  }

  /**
   * 生产者发布事件
   */
  public void produce(String message) {
    messagemEventProducer.onData(message);
  }

}
7、测试程序类型:App.java,用于测试
/**
* for test
*
* @author yang.kuowei
* @version 1.0
* @date 2019-05-14 19:49
*/
public class App {

  @Test
  public void disruptorTest() {
    //获取实例
    DisruptorUtil disruptorUtil = DisruptorUtil.getInstance();
    //启动
    disruptorUtil.start();
    //发布消息
    disruptorUtil.produce("disruptor test !");
  }
}
8、测试结果:

在这里插入图片描述

二、核心概念

下面介绍的概念模型,既是领域对象,也是映射到代码实现上的核心对象。

  • Ring Buffer
    如其名,环形的缓冲区。曾经 RingBuffer 是 Disruptor 中的最主要的对象,但从3.0版本开始,其职责被简化为仅仅负责对通过 Disruptor 进行交换的数据(事件)进行存储和更新。在一些更高级的应用场景中,Ring Buffer 可以由用户的自定义实现来完全替代。

  • Sequence Disruptor
    通过顺序递增的序号来编号管理通过其进行交换的数据(事件),对数据(事件)的处理过程总是沿着序号逐个递增处理。一个 Sequence 用于跟踪标识某个特定的事件处理者( RingBuffer/Consumer )的处理进度。虽然一个 AtomicLong 也可以用于标识进度,但定义 Sequence 来负责该问题还有另一个目的,那就是防止不同的 Sequence 之间的CPU缓存伪共享(Flase Sharing)问题。

  • Sequencer
    Sequencer 是 Disruptor 的真正核心。此接口有两个实现类 SingleProducerSequencer、MultiProducerSequencer ,它们定义在生产者和消费者之间快速、正确地传递数据的并发算法。

  • Sequence Barrier
    用于保持对RingBuffer的 main published Sequence 和Consumer依赖的其它Consumer的 Sequence 的引用。 Sequence Barrier 还定义了决定 Consumer 是否还有可处理的事件的逻辑。

  • Wait Strategy
    定义 Consumer 如何进行等待下一个事件的策略。
    各种策略名称及使用场景如下:

    BlockingWaitStrategy 加锁 CPU资源紧缺,吞吐量和延迟并不重要的场景
    BusySpinWaitStrategy 自旋 通过不断重试,减少切换线程导致的系统调用,而降低延迟。推荐在线程绑定到固定的CPU的场景下使用
    PhasedBackoffWaitStrategy 自旋 + yield + 自定义策略 CPU资源紧缺,吞吐量和延迟并不重要的场景
    SleepingWaitStrategy 自旋 + yield + sleep 性能和CPU资源之间有很好的折中。延迟不均匀
    TimeoutBlockingWaitStrategy 加锁,有超时限制 CPU资源紧缺,吞吐量和延迟并不重要的场景
    YieldingWaitStrategy 自旋 + yield + 自旋 性能和CPU资源之间有很好的折中。延迟比较均匀

  • Event
    在 Disruptor 的语义中,生产者和消费者之间进行交换的数据被称为事件(Event)。它不是一个被 Disruptor 定义的特定类型,而是由 Disruptor 的使用者定义并指定。

  • EventProcessor
    EventProcessor 持有特定消费者(Consumer)的 Sequence,并提供用于调用事件处理实现的事件循环(Event Loop)。

  • EventHandler
    Disruptor 定义的事件处理接口,由用户实现,用于处理事件,是 Consumer 的真正实现。

  • Producer
    即生产者,只是泛指调用 Disruptor 发布事件的用户代码,Disruptor 没有定义特定接口或类型。

三、说明

  • 在构建Disruptor实例的时候,需要指定生产者是单生产者(ProducerType.SINGLE)还是多生产者(ProducerType.MULTI)
  • 消费者可以广播消费(所有消费者都有机会消费相同的消息使用handleEventsWith方法),也可以集群消费(每个消费者消费不同数据,使用handleEventsWithWorkerPool方法)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值