Disruptor是一个开源的并发框架,并获得2011年Duke's程序框架创新奖,能够在无锁的情况下实现网络的Queue队列并发操作。Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。也可以理解为一个高效的“生产者-消费者”模型。性能远远高于传统的BlockingQueue。
Disruptor的Ringbuffer模型:
分析:disruptor内部维护的Ringbuffer其实结构是一个首尾相接的环形,用作在多线程之间传递数据的buffer,ringbuffer有一个序号sequence,这个序号sequence指向数组中下一个可用的元素。随着生产者不停填充这个ringbuffer,这个序号sequence可能会一直增长,直到绕过这个环。如上图,生产者向一个容量为8的ringbuffer环中填充了8个数据从1到8,当sequence为0的元素1被消费者消费掉后,生产者就将下一个元素放置到原来存放1的sequence为0的位置上,但是此时的sequence不是0,而是接着增长到了9,这是因为ringbuffer维护的是一个AutomicLong的数值来表示sequence。
只要知道了sequence序号,就可以通过取模的方式得到元素在ringbuffer环中的位置:
sequence mod array length = arrayIndex
以上边图中的ringbuffer为例,如果sequence=8,那么8%8=0,这样消费者就可以通过下标0拿到ringbuffer环数组的元素。
注意:上图中ringbuffer只有8个槽只是个例子,如果槽的个数是2的N次方,则会更有利于二进制计算机计算。
理解RingBuffer:
1 没有尾指针:Disruptor的ringbuffer没有尾指针,只维护了一个指向下一个可用位置的序号sequence,这种实现是设计者经过深思熟虑的,设计之初就是为了提供可靠的消息传递。
2 不需要删除元素:RingBuffer和常用队列区别是,我们不能删除RingBuffer中的数据,也就是说这些数据一直存放在RingBuffer中,直到新的数据覆盖它们,这也是之所以不需要尾指针的原因,因为根本不需要控制数据的覆盖。
3 RingBuffer是数组,所以比链表查询速度快,而且有一个可以预测的访问模式。
数组是对CPU友好的,在硬件层面,数组的元素是会被预加载的,因此在RingBuffer中,cpu不需要时不时的加载数据中下一个元素。
Disruptor的术语说明
WaitStrategy:决定消费者如何等待生产者将Event放入Disruptor。
Event:从生产者到消费者所处理的数据单元。Disruptor中没有代码定义Event,因此它完全是由用户来定义的。
EventProcessor:主要事件循环,处理Disruptor中的Event,并且拥有消费者的sequence。它有一个实现类是BatchEventProcessor,包含了Event loop有效的实现,并且将回调到一个EventHandler的接口实现对象。
EventHandler:由用户实现并且代表了Disruptor中的一个消费者的接口。
Producer:事件生产者,由用户实现,它调用Ringbuffer来插入事件Event,在Disruptor没有相应实现代码,由用户实现。
WorkProcessor:确保每一个sequence只被一个Processor消费,在同一个workPool中处理多个workProcessor不会消费同样的sequence。
WorkerPool:一个workProcessor池,其中workProcessor将消费Sequence,所以任务可以在实现WorkHandler接口的worker之间移交。
LifeCycleAware:由BatchEventProcessor启动和停止时实现这个接口用于接收通知。
HelloWorld代码实现:
LongEvent事件对象
-
//http://ifeve.com/disruptor-getting-started/
-
/**
-
* 生产者要生产的【event对象】,主动传递给disruptor中的RingBuffer
-
* @author jeffSheng
-
*
-
*/
-
public class LongEvent {
-
private long value;
-
public long getValue() {
-
return value;
-
}
-
public void setValue(long value) {
-
this.value = value;
-
}
-
}
LongEventFactory事件工厂
-
import com.lmax.disruptor.EventFactory;
-
/**
-
* 实现disruptor的【事件工厂】EventFactory,让disruptor批量产生longEvent
-
* @author jeffSheng
-
*
-
*/
-
public class LongEventFactory implements EventFactory {
-
@Override
-
public Object newInstance() {
-
return new LongEvent();
-
}
-
}
LongEventHandler事件处理器( 消费者)
-
/**
-
* 定义disruptor的【消费者】
-
* 我们还需要一个事件消费者实现disruptor的EventHandler,也就是一个事件处理器。
-
* 这个事件处理器简单地把事件中存储的数据打印到终端:
-
* @author jeffSheng
-
*
-
*/
-
public class LongEventHandler implements EventHandler<LongEvent> {
-
@Override
-
public void onEvent(LongEvent longEvent, long l, boolean b) throws Exception {
-
System.out.println(longEvent.getValue());
-
}
-
}
主函数:
-
import java.nio.ByteBuffer;
-
import java.util.concurrent.ExecutorService;
-
import java.util.concurrent.Executors;
-
import com.lmax.disruptor.RingBuffer;
-
import com.lmax.disruptor.YieldingWaitStrategy;
-
import com.lmax.disruptor.dsl.Disruptor;
-
import com.lmax.disruptor.dsl.ProducerType;
-
public class LongEventMain {
-
public static void main(String[] args) throws Exception {
-
//创建缓冲线程池
-
ExecutorService executor = Executors.newCachedThreadPool();
-
//创建LongEvent事件工厂
-
LongEventFactory factory = new LongEventFactory();
-
//创建bufferSize缓冲区 ,也就是RingBuffer大小,要求必须是2的N次方
-
int ringBufferSize = 1024 * 1024;
-
/**
-
* 创建disruptor实例,并传入泛型LongEvent事件类型(数据类型)
-
* 构造参数:
-
* Disruptor(
-
* factory,--事件工厂,用于创建LongEvent,也就是实际最终被消费的数据
-
* ringBufferSize,--缓冲区大小
-
* executor,--线程池,作用是使用线程池进行内部数据接收处理调度
-
* producerType,--两种形式:SINGLE(生产者只有一个)和MULTI(生产者有多个)
-
* waitStrategy--决定一个消费者将如何等待生产者将EVENT放入disruptor
-
* )
-
* //BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小,并且在各种不同部署环境中能提供更加一致的性能表现
-
WaitStrategy BLOCKING_WAIT = new BlockingWaitStrategy();
-
//SleepingWaitStrategy 的性能表现跟BlockingWaitStrategy差不多,对CPU的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景
-
WaitStrategy SLEEPING_WAIT = new SleepingWaitStrategy();
-
//YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于CPU逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性
-
WaitStrategy YIELDING_WAIT = new YieldingWaitStrategy();
-
*/
-
Disruptor<LongEvent> disruptor =
-
new Disruptor<LongEvent>(factory, ringBufferSize, executor, ProducerType.SINGLE, new YieldingWaitStrategy());
-
/**
-
* 连接消费事件方法,监听ringbuffer环形队列容器中的事件,有则取出消费,无则阻塞,所以disruptor相当于一个特殊的有界阻塞队列
-
* LongEventHandler 理解为数据消费者
-
*/
-
disruptor.handleEventsWith(new LongEventHandler());
-
// 启动disruptor
-
disruptor.start();
-
//Disruptor 的事件发布过程是一个两阶段提交的过程:
-
/**
-
* 发布事件
-
* 使用该方法获得具体存放数据的容器ringBuffer(环形结构)
-
*/
-
RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
-
//定义事件生产者
-
LongEventProducer producer = new LongEventProducer(ringBuffer);
-
// LongEventProducerWithTranslator producer = new LongEventProducerWithTranslator(ringBuffer);
-
//定义一个含有8个空间的字节缓冲
-
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
-
for(long a = 0; a < 100; a++){
-
byteBuffer.putLong(0, a);//每次覆盖byteBuffer下标为0的位置
-
//producer将数据byteBuffer存入ringbuffer事件槽
-
producer.onData(byteBuffer);
-
//Thread.sleep(1000);
-
}
-
disruptor.shutdown();//关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;
-
executor.shutdown();//关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭, disruptor 在 shutdown 时不会自动关闭;
-
}
-
}
主函数代码分析
实现HelloWorld的步骤:
第一,建立Event类
第二,建立一个Event工厂类,用户实例化Event对象。
第三,需要一个事件监听器,用于处理Event类
以上三步:
-
//创建缓冲线程池
-
ExecutorService executor = Executors.newCachedThreadPool();
-
//创建LongEvent事件工厂
-
LongEventFactory factory = new LongEventFactory();
-
//创建bufferSize缓冲区 ,也就是RingBuffer大小,要求必须是2的N次方
-
int ringBufferSize = 1024 * 1024;
第四,编写主函数,实例化Disruptor实例,传入构造参数,然后disruptor绑定监听事件类,接收并处理数据。
创建disruptor实例,并传入泛型LongEvent事件类型(数据类型)
构造参数:
new Disruptor(
factory,--事件工厂,用于创建LongEvent,也就是实际最终被消费的数据
ringBufferSize,--缓冲区大小
executor,--线程池,作用是使用线程池进行内部数据接收处理调度
producerType,--两种形式:SINGLE(生产者只有一个)和MULTI(生产者有多个)
waitStrategy--决定一个消费者将如何等待生产者将EVENT放入disruptor
)
-
Disruptor<LongEvent> disruptor =
-
new Disruptor<LongEvent>(factory, ringBufferSize, executor, ProducerType.SINGLE, new YieldingWaitStrategy());
-
/**
-
* 连接消费事件方法,监听ringbuffer环形队列容器中的事件,有则取出消费,无则阻塞,所以disruptor相当于一个特殊的有界阻塞队列
-
* LongEventHandler 理解为数据消费者
-
*/
-
disruptor.handleEventsWith(new LongEventHandler());
-
// 启动disruptor
-
disruptor.start();
第五,在disruptor中,真正存储数据的核心是Ringbuffer,我们通过disruptor实例拿到了它,然后把数据生产了出来,把数据加入到Ringbuffer中即可。
-
//定义事件生产者
-
LongEventProducer producer = new LongEventProducer(ringBuffer);
-
//定义一个含有8个空间的字节缓冲
-
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
-
for(long a = 0; a < 100; a++){
-
byteBuffer.putLong(0, a);//每次覆盖byteBuffer下标为0的位置
-
//producer将数据byteBuffer存入ringbuffer事件槽
-
producer.onData(byteBuffer);
-
//Thread.sleep(1000);
-
}
-
disruptor.shutdown();//关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;
-
executor.shutdown();//关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭, disruptor 在 shutdown 时不会自动关闭;
生产者定义方式一:
-
import java.nio.ByteBuffer;
-
import com.lmax.disruptor.RingBuffer;
-
public class LongEventProducer {
-
private final RingBuffer<LongEvent> ringBuffer;
-
public LongEventProducer(RingBuffer<LongEvent> ringBuffer){
-
this.ringBuffer = ringBuffer;
-
}
-
/**
-
* onData用来发布事件,每调用一次就发布一次事件, 它的参数bb会通过事件传递给消费者
-
* 将bb存入ringBuffer环形事件容器
-
* 生产数据需要遵循的四个步骤
-
*/
-
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));
-
} finally {
-
//4.发布事件
-
/**
-
* 注意,最后的 ringBuffer.publish 方法必须包含在 finally 中以确保必须得到调用;
-
* 如果某个请求的 sequence 未被提交,将会堵塞后续的发布操作或者其它的 producer。
-
* 只有发布后才能被监听的消费者消费掉
-
*/
-
ringBuffer.publish(sequence);
-
}
-
}
-
}
生产者定义方式二:
-
import java.nio.ByteBuffer;
-
import com.lmax.disruptor.EventTranslatorOneArg;
-
import com.lmax.disruptor.RingBuffer;
-
/**
-
* Disruptor 3.0提供了lambda式的API。这样可以把一些复杂的操作放在Ring Buffer,
-
* 所以在Disruptor3.0以后的版本最好使用Event Publisher或者Event Translator来发布事件
-
*/
-
public class LongEventProducerWithTranslator {
-
//一个translator可以看做一个事件初始化器,publicEvent方法会调用它
-
//填充Event
-
private static final EventTranslatorOneArg<LongEvent, ByteBuffer> TRANSLATOR =
-
new EventTranslatorOneArg<LongEvent, ByteBuffer>() {
-
@Override
-
public void translateTo(LongEvent event, long sequeue, ByteBuffer buffer) {
-
event.setValue(buffer.getLong(0));
-
}
-
};
-
private final RingBuffer<LongEvent> ringBuffer;
-
public LongEventProducerWithTranslator(RingBuffer<LongEvent> ringBuffer) {
-
this.ringBuffer = ringBuffer;
-
}
-
public void onData(ByteBuffer buffer){
-
ringBuffer.publishEvent(TRANSLATOR, buffer);
-
}
-
}
结果打印:控制台从0打印到99.