网页右边,向下滑有目录索引,可以根据标题跳转到你想看的内容 |
---|
如果右边没有就找找左边 |
在马士兵教育笔记基础上更改 |
---|
作者:马士兵 http://www.mashibing.com
最近更新:2019年10月22日
1、介绍
GettingStarted: https://github.com/LMAX-Exchange/disruptor/wiki/Getting-Started
api: http://lmax-exchange.github.io/disruptor/docs/index.html
maven: https://mvnrepository.com/artifact/com.lmax/disruptor
Disruption:分裂,瓦解(表示非常快) |
---|
简单来说,就是内存里面的一个生产各种高效队列的工具 |
据说一个线程1秒可以处理600万订单 |
2011年获得Duke将(框架的奖项) |
速度最快的MQ(消息队列) |
性能极高,无锁CAS,单机支持高并发 |
个人总结 |
---|
此框架,可以根据不同条件生产高效队列 |
生产高效队列时,需要先指定,Event工厂,队列大小,Event消费者 |
Event工厂:每个队列都存储一样的元素,这些元素称为事件Event,工厂用来生成对象 |
Event消费者:规定队列中元素如何消费 |
生产队列时,先根据队列大小创建空间,然后根据Event工厂,为每个元素空间赋值,这样就有了一个固定大小,已经填满特定Event的队列了 |
之后,如果有元素插入,不需要new对象,如果队列满了,新来的元素要插入,也不需要new对象,直接覆盖就可以了 |
如何确定插入位置:通过一个位置指针,按公式num&(size-1)计算插入位置 |
2、Disruptor的特点
对比ConcurrentLinkedQueue : Concurrent表示高并发的,Linked表示它是链表实现的
JDK中没有ConcurrentArrayQueue:也就是说没有数组的高并发队列,但是遍历的时候,数组一定比链表快
为什么JDK不加数组实现的队列呢?因为一旦数组满了,想要添加新元素,必须拷贝一份,添加完再建立引用,这样的效率极低
Disruptor就是数组实现的,但是它解决数组达到上限需要拷贝的问题,转而将其想象成环形结构,并维护一个位置指针,一旦下一个指针指向位置超过最大,就执行算法,重新执行第一个位置,然后新插入的值,覆盖当前位置
无锁,高并发,使用环形Buffer,直接覆盖(不用清除)旧的数据,降低GC频率
实现了基于事件的生产者消费者模式(观察者模式)
3、RingBuffer
环形队列
RingBuffer的序号,指向下一个可用的元素
采用数组实现,没有首尾指针
对比ConcurrentLinkedQueue,用数组实现的速度更快
假如长度为8,当添加到第12个元素的时候在哪个序号上呢?用12%8决定
当Buffer被填满的时候到底是覆盖还是等待,由Producer(等待策略)决定
强制长度应该设为2的n次幂
,利于二进制计算,例如:12%8 = 12 & (8 - 1) 公式就是 pos = num元素序号 & (size最大容量 -1)
那么如果长度达到了,位置重新指向初始位置,此时发现初始位置的元素,没有被消费,那么新来的元素是插入还是等待,就需要用等待策略,Disruptor自带一共8种等待策略
4、Disruptor开发步骤
- 定义Event(事件) - 队列中需要处理的元素,队列中存的都是一个一个的Event对象,一个一个事件
//Event事件
public class LongEvent {
private long value;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
- 定义Event工厂,用于填充队列,Event的创建需要用到工厂
这里牵扯到效率问题:disruptor初始化的时候,会调用Event工厂,对ringBuffer进行内存的提前分配(就是看到工厂创建的是指定的Event对象,那么容器就提前分配好对象,),之后插入数据,不用频繁new对象,而是覆盖的方式插入
GC频率会降低,提高性能
import com.lmax.disruptor.EventFactory;
//使用工厂创建Event事件,需要实现EventFactory继承来的接口
public class LongEventFactory implements EventFactory<LongEvent> {
@Override
public LongEvent newInstance() {
return new LongEvent();
}
}
- 定义EventHandler(消费者),处理容器中的元素,我们需要指定消费者如何消费队列中的Event事件
其实这些东西都是函数式编程,可以通过lambda来用比如,创建消费者时可以直接
disruptor.handleEventsWith((longEvent,l,b)->{System.out.println(longEvent.getValue());});
import com.lmax.disruptor.EventHandler;
//Event消费者,继承EventHandler,实现onEvent方法
public class LongEventHandler implements EventHandler<LongEvent> {
/**
*
* @param longEvent 消费哪个小学
* @param l 哪个位置的消息
* @param b 是否是最后一个元素
* @throws Exception
*/
@Override
public void onEvent(LongEvent longEvent, long l, boolean b) throws Exception {
System.out.println(longEvent.getValue());
}
}
- 定义EventProducer(等待策略),前面我们说过,队列满了,一个元素要插入是覆盖还是等待
可以自定义,或者使用它自己提供的9种
import com.lmax.disruptor.RingBuffer;
import java.nio.ByteBuffer;
public class LongEventProducer {
private final RingBuffer<LongEvent> ringBuffer;
public LongEventProducer(RingBuffer<LongEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void onData(ByteBuffer buffer) {
long sequence = ringBuffer.next();
try {
LongEvent event = ringBuffer.get(sequence);
event.setValue(buffer.getLong(0));
} finally {
ringBuffer.publish(sequence);
}
}
}
- 编写测试类
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.EventTranslator;
import com.lmax.disruptor.EventTranslatorOneArg;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.sun.org.apache.xpath.internal.functions.Function3Args;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
//Executor executor = Executors.newCachedThreadPool();
//创建工厂对象
LongEventFactory factory = new LongEventFactory();
//指定环形队列大小,必须是2的倍数,2的n次幂
int ringBufferSize = 1024;
//创建Disruption对象,传入工厂对象,队列大小,以及线程工厂(当它要产生消费者时,消费者需要一个线程执行,此参数规定生产什么样的线程)
Disruptor<LongEvent> disruptor = new Disruptor<LongEvent>(factory, ringBufferSize, Executors.defaultThreadFactory(),ProducerType.SINGLE,new BlockingWaitStrategy());
//拿到事件后,如何进行处理,传入Event消费者对象,规定,消费者拿到事件后如何消费
disruptor.handleEventsWith(new LongEventHandler());
// disruptor.handleEventsWith((longEvent,l,b)->{System.out.println(longEvent.getValue());});//lambda表达式形式,不用new那么多类
//队列开始,内存中一个环形队列生产完成,每个元素都已经根据工厂生产完成
disruptor.start();
//获取生产好的队列
RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
//官方案例,这样写不能用lambda
System.out.println("==================普通的发布事件,让消费者来消费====================");
long sequence = ringBuffer.next();//获取下一个位置
try{
LongEvent longEvent = ringBuffer.get(sequence);//获取指定位置元素,为其设置值等等
longEvent.setValue(8888L);//获取元素后,便可以对其操作
}finally{
ringBuffer.publish(sequence);//将这个位置的元素,事件发布出去,让消费者可以获取,获取后会执行消费者的逻辑
}
System.out.println("==================通过EventTranslator来发布事件,让消费者来消费====================");
//我们可以通过定义eventTranslator来实现上面操作,并且方便很多,这个是写死了值的
EventTranslator<LongEvent> eventTranslator = new EventTranslator<LongEvent>() {
@Override
public void translateTo(LongEvent event, long sequence) {
event.setValue(8888L);
}
};
ringBuffer.publishEvent(eventTranslator);//发布
//下面这个可以指定一个参数
EventTranslatorOneArg<LongEvent, Long> eventTranslatorOneArg = new EventTranslatorOneArg<LongEvent, Long>() {
@Override
public void translateTo(LongEvent event, long sequence, Long arg0) {
event.setValue(arg0);
}
};
ringBuffer.publishEvent(eventTranslatorOneArg,10L);//发布
//可以直接用lambda指定发布
System.out.println("==================lambda发布事件,让消费者来消费(注意lambda如何指定参数)====================");
ringBuffer.publishEvent((e,s)->{e.setValue(8889L);});//发布
ringBuffer.publishEvent((e,s,l)->e.setValue(l),11L);//发布
ringBuffer.publishEvent((e,s,l1,l2)->e.setValue(l1+l2),11L,22L);//发布
//获取等待策略
LongEventProducer producer = new LongEventProducer(ringBuffer);
//字节缓存
ByteBuffer bb = ByteBuffer.allocate(8);
for(long l = 0; l<100; l++) {
bb.putLong(0, l);
producer.onData(bb);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
disruptor.shutdown();//关闭Disruptor
//
}
}
5、事件发布模板
long sequence = ringBuffer.next(); // Grab the next sequence
try {
LongEvent event = ringBuffer.get(sequence); // Get the entry in the Disruptor
// for the sequence
event.set(8888L); // Fill with data
} finally {
ringBuffer.publish(sequence);
}
6、使用EventTranslator发布事件
System.out.println("==================通过EventTranslator来发布事件,让消费者来消费====================");
//我们可以通过定义eventTranslator来实现上面操作,并且方便很多,这个是写死了值的
EventTranslator<LongEvent> eventTranslator = new EventTranslator<LongEvent>() {
@Override
public void translateTo(LongEvent event, long sequence) {
event.setValue(8888L);
}
};
ringBuffer.publishEvent(eventTranslator);//发布
//下面这个可以指定一个参数
EventTranslatorOneArg<LongEvent, Long> eventTranslatorOneArg = new EventTranslatorOneArg<LongEvent, Long>() {
@Override
public void translateTo(LongEvent event, long sequence, Long arg0) {
event.setValue(arg0);
}
};
ringBuffer.publishEvent(eventTranslatorOneArg,10L);//发布
//===============================================================
EventTranslatorTwoArg<LongEvent, Long, Long> translator3 = new EventTranslatorTwoArg<LongEvent, Long, Long>() {
@Override
public void translateTo(LongEvent event, long sequence, Long l1, Long l2) {
event.set(l1 + l2);
}
};
ringBuffer.publishEvent(translator3, 10000L, 10000L);
//===============================================================
EventTranslatorThreeArg<LongEvent, Long, Long, Long> translator4 = new EventTranslatorThreeArg<LongEvent, Long, Long, Long>() {
@Override
public void translateTo(LongEvent event, long sequence, Long l1, Long l2, Long l3) {
event.set(l1 + l2 + l3);
}
};
ringBuffer.publishEvent(translator4, 10000L, 10000L, 1000L);
//===============================================================
EventTranslatorVararg<LongEvent> translator5 = new EventTranslatorVararg<LongEvent>() {
@Override
public void translateTo(LongEvent event, long sequence, Object... objects) {
long result = 0;
for(Object o : objects) {
long l = (Long)o;
result += l;
}
event.set(result);
}
};
ringBuffer.publishEvent(translator5, 10000L, 10000L, 10000L, 10000L);
7、使用Lamda表达式
public class Main03
{
public static void main(String[] args) throws Exception
{
// Specify the size of the ring buffer, must be power of 2.
int bufferSize = 1024;
// Construct the Disruptor
Disruptor<LongEvent> disruptor = new Disruptor<>(LongEvent::new, bufferSize, DaemonThreadFactory.INSTANCE);
// Connect the handler
disruptor.handleEventsWith((event, sequence, endOfBatch) -> System.out.println("Event: " + event));
// Start the Disruptor, starts all threads running
disruptor.start();
// Get the ring buffer from the Disruptor to be used for publishing.
RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
//可以直接用lambda指定发布
System.out.println("==================lambda发布事件,让消费者来消费(注意lambda如何指定参数)====================");
ringBuffer.publishEvent((e,s)->{e.setValue(8889L);});//发布
ringBuffer.publishEvent((e,s,l)->e.setValue(l),11L);//发布
ringBuffer.publishEvent((e,s,l1,l2)->e.setValue(l1+l2),11L,22L);//发布
}
}
8、ProducerType生产者线程模式
//创建Disruption对象,传入工厂对象,队列大小,以及线程工厂,指定生产者线程模式(指定它必须在后面也指定等待策略)
Disruptor<LongEvent> disruptor = new Disruptor<LongEvent>(factory, ringBufferSize, Executors.defaultThreadFactory(), ProducerType.SINGLE,,new BlockingWaitStrategy());
ProducerType有两种模式 Producer.MULTI和Producer.SINGLE
默认是MULTI,表示在多线程模式下产生sequence
如果确认是单线程生产者,那么可以指定SINGLE,效率会提升
如果是多个生产者(多线程),但模式指定为SINGLE,会出什么问题呢?
9、等待策略
1,(常用)BlockingWaitStrategy:通过线程阻塞的方式,等待生产者唤醒,被唤醒后,再循环检查依赖的sequence是否已经消费。
2,BusySpinWaitStrategy:线程一直自旋等待,可能比较耗cpu
3,LiteBlockingWaitStrategy:线程阻塞等待生产者唤醒,与BlockingWaitStrategy相比,区别在 signalNeeded.getAndSet,如果两个线程同时访问一个访问waitfor,一个访问signalAll时,可以减少lock加锁次数.
4,LiteTimeoutBlockingWaitStrategy:与LiteBlockingWaitStrategy相比,设置了阻塞时间,超过时间后抛异常。
5,PhasedBackoffWaitStrategy:根据时间参数和传入的等待策略来决定使用哪种等待策略
6,TimeoutBlockingWaitStrategy:相对于BlockingWaitStrategy来说,设置了等待时间,超过后抛异常
7,(常用)YieldingWaitStrategy:尝试100次,然后Thread.yield()让出cpu
8,(常用)SleepingWaitStrategy : sleep
10、 消费者异常处理
默认:disruptor.setDefaultExceptionHandler()
覆盖:disruptor.handleExceptionFor().with()