Disruptor高性能并发框架

扯一下读书

自从实习以后,我对读书是越来越情有独钟了,特别是一些有关时间管理,逻辑思维训练,心理学之类的书。读书是我工作和生活之外的一种补充,是一种调节剂。至于社会上充斥的一些"读书无用论"的观点,我觉得一笑而过就算了。借用三毛姐的一句话,就是:"读书多了,容颜自然改变,许多时候,自己可能以为许多看过的书籍都成为了过眼云烟,不复记忆,其实它们是潜在的。在气质里,在谈吐上,在胸襟的无涯,当然也可能显露在生活和文字里。当读书的习惯成为自然后,你就不会去思考它的用处了"。

背景

高并发是指通过设计保证系统能够同时并行处理很多请求。虽然我在工作中经常听到高并发,QPS之类的术语。其实,我对高并发也是一知半解,知道Java里面可以用Lock,Synchronized,ArrayBlockingQueue之类的来进行高并发的处理。我个人觉得,高并发领域更多的依靠的是经验的累积。今天想跟大家分享的是一个高性能的并发框架---Disruptor。

Disruptor并发框架(Disruptor版本为3.2.1)

目录

  • 目标
  • Disruptor概述
  • 一个官网的简单的demo
    • maven依赖
    • 创建数据实体类LongEvent
    • 创建工厂类LongEventFactory
    • 创建消费者类LongEventHandler
    • 创建生产者类LongEventProducer
    • 创建测试类 LongEventMain
    • demo结果输出
  • Disruptor的一些核心介绍
    • RingBuffer
    • 消费者等待策略
    • Disruptor的应用场景

一 目标

了解Disruptor的基本概念并且实现一个官网的demo。

二 Disruptor概述

  1. Disruptor是一个异步并发处理框架。是由LMAX公司开发的一款高效的无锁内存队列。它使用无锁的方式实现了一个环形队列,非常适合于实现生产者和消费者模式,比如事件和消息的发布。
  2. Disruptor最大特点是高性能,其LMAX架构可以获得每秒6百万订单,用1微秒的延迟获得吞吐量为100K+。

三 一个官网的简单的demo

1.maven依赖

maven引入Disruptor的jar包,Disruptor版本为3.2.1

<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.2.1</version>
</dependency>

2.创建数据实体类LongEvent

创建数据实体类LongEvent

//代表数据的类
public class LongEvent
{
private long value;

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

3.创建工厂类LongEventFactory

创建工厂类LongEventFactory

//产生LongEvent的工厂类,它会在Disruptor系统初始化时,构造所有的缓冲区中的对象实例(预先分配空间)
public class LongEventFactory implements EventFactory<LongEvent>
{
public LongEvent newInstance()
{
return new LongEvent();
}
}

4. 创建消费者类LongEventHandler

创建消费者类LongEventHandler

//消费者实现为WorkHandler接口,是Disruptor框架中的类
public class LongEventHandler implements EventHandler<LongEvent>
{
//onEvent()方法是框架的回调用法
public void onEvent(LongEvent event, long sequence, boolean endOfBatch)
{
System.out.println("Event: " + event);
}
}

5. 创建生产者类LongEventProducer

创建生产者类LongEventProducer

public class LongEventProducer
{
//环形缓冲区,装载生产好的数据;
private final RingBuffer<LongEvent> ringBuffer;

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

//将数据推入到缓冲区的方法:将数据装载到ringBuffer
public void onData(ByteBuffer bb)
{
long sequence = ringBuffer.next(); // Grab the next sequence //获取下一个可用的序列号
try
{
LongEvent event = ringBuffer.get(sequence); // Get the entry in the Disruptor //通过序列号获取空闲可用的LongEvent
// for the sequence
event.set(bb.getLong(0)); // Fill with data //设置数值
}
finally
{
ringBuffer.publish(sequence); //数据发布,只有发布后的数据才会真正被消费者看见
}
}
}

6. 创建测试类 LongEventMain

创建测试类 LongEventMain

public class LongEventMain
{
public static void main(String[] args) throws Exception
{
// Executor that will be used to construct new threads for consumers
//创建线程池
Executor executor = Executors.newCachedThreadPool();

// The factory for the event
//事件工厂
LongEventFactory factory = new LongEventFactory();

// Specify the size of the ring buffer, must be power of 2.
//ringBuffer 的缓冲区的大小是1024
int bufferSize = 1024;

// Construct the Disruptor
//创建一个disruptor
//ProducerType.MULTI:创建一个环形缓冲区支持多事件发布到一个环形缓冲区
Disruptor<LongEvent> disruptor = new Disruptor<>(factory, bufferSize, executor,ProducerType.MULTI,new BlockingWaitStrategy());

// Connect the handler
//创建一个消费者
disruptor.handleEventsWith(new LongEventHandler());

// Start the Disruptor, starts all threads running
//启动并初始化disruptor
disruptor.start();

// Get the ring buffer from the Disruptor to be used for publishing.
//获取已经初始化好的ringBuffer
RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();

获取已经初始化好的ringBuffer
LongEventProducer producer = new LongEventProducer(ringBuffer);

ByteBuffer bb = ByteBuffer.allocate(8);
for (long l = 0; true; l++)
{
//存入数据
bb.putLong(0, l);
producer.onData(bb);
Thread.sleep(1000);
}
}
}

7.demo结果输出

demo结果输出(因为是一个死循环,会不断的输出)

Event: com.meizu.in.appstore.disruptor3.LongEvent@1223929e
Event: com.meizu.in.appstore.disruptor3.LongEvent@407afb9
Event: com.meizu.in.appstore.disruptor3.LongEvent@237bc42c
Event: com.meizu.in.appstore.disruptor3.LongEvent@7cb5a7bb
Event: com.meizu.in.appstore.disruptor3.LongEvent@720c6dd1
Event: com.meizu.in.appstore.disruptor3.LongEvent@4b4f6e4e
Event: com.meizu.in.appstore.disruptor3.LongEvent@366889af

Disruptor的一些核心介绍

1.RingBuffer

  1. RingBuffer是其核心,生产者向RingBuffer中写入元素,消费者从RingBuffer中消费元素。
  2. 随着你不停地填充这个buffer(可能也会有相应的读取),这个序号会一直增长,直到绕过这个环。
  3. 槽的个数是2的N次方更有利于基于二进制的计算机进行计算。(注:2的N次方换成二进制就是1000,100,10,1这样的数字, sequence & (array length-1) = array index,比如一共有8槽,3&(8-1)=3,HashMap就是用这个方式来定位数组元素的,这种方式比取模的速度更快。)
  4. 会预先分配内存,可以做到完全的内存复用。在系统的运行过程中,不会有新的空间需要分配或者老的空间需要回收。因此,可以大大减少系统分配空间以及回收空间的额外开销。
  5. 关于RingBuffer可以直观的看一下下面的这幅图片(网上copy的),表示取到编号为4的数据。

2.消费者等待策略

  1. BlockingWaitStrategy:这是默认的策略。使用锁和条件进行数据的监控和线程的唤醒。因为涉及到线程的切换,是最节省CPU,但在高并发下性能表现最糟糕的一种等待策略。
  2. SleepingWaitStrategy:会自旋等待数据,如果不成功,才让出cpu,最终进行线程休眠,以确保不占用太多的CPU数据,因此可能产生比较高的平均延时。比较适合对延时要求不高的场合,好处是对生产者线程的影响最小。典型的应用场景是异步日志。
  3. YieldingWaitStrategy:用于低延时的场合。消费者线程不断循环监控缓冲区变化,在循环内部,会使用Thread.yield()让出cpu给别的线程执行时间。
  4. BusySpinWaitStrategy:开启的是一个死循环监控,消费者线程会尽最大努力监控缓冲区变化,因此,CPU负担比较大

3.Disruptor的应用场景

Disruptor号称能够在一个线程里每秒处理 6 百万订单,实际上我也没有测试过。Disruptor实际上内部是使用环形队列来实现的,所以一般来说,在消费者和生产者的场景中都可以考虑使用Disruptor。比如像日志处理之类的。实际上,我个人觉得Disruptor就像是Java里面的ArrayBlockingQueue的替代者,因为Disruptor可以提供更高的并发度和吞吐量。从下面这幅官网的图就可以直观的感受到Disruptor和ArrayBlockingQueue之间的效率的对比。(注意这是一个对数对数尺度,不是线性的。)

感谢&总结

在此首先感谢公司同事关于Disruptor的分享。关于本文主要分享了如何去应用Disruptor并发框架,并且对一些核心原理进行了阐述。古人说,"日拱一卒无有尽 功不唐捐终入海",每天进步一点点,量变引起质变,最后,我们还是可以取得长足的进步的,共勉。

  • 3
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱coding的同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值