一、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 时不会自动关闭;
}
}