Disruptor:
介绍:是一个高性能的异步处理框架,研发的初衷是为了解决高并发情况下队列加锁的问题,使用效果和BlockingQueue很类似,号称“一个线程一秒可以处理600万订单”(惊呆了);
核心设计组件:
RingBuffer:环形缓冲区,其内部定义了数组、sequencer等属性,利用数组存储结构达到了环形数组的效果;但存储的数组存储达或获取到最后一位时,又开始重第一位开始存储获取获取;
无锁设计:每个生产者或消费者会先申请可以操作的元素在数组中的位置,申请到之后,直接在该下标位置进行读取或写入;
数组的长度只能是2的指数次幂,利用位运算,加快下标的定位;
sequence序号:利用生产者和消费者维护的序号来定位ringBuffer的位置,并且这个sequence是一直递增的,添加一个元素或消费一个元素,对应消费者和生产者各自维护的sequence就会加1;比如说一个RingBuffer的大小是8,序号从0开始增长,当sequence=7时,相当于转了一圈,如果继续增长的话,下一个获取或者写入的下标会从0开始;也即是说sequence%ringBuffer.length取模计算出操作ringBuffer的下标;
由于涉及到取模操作,没有位运算高效,所以为了使用位运算,定义RingBuffer的大小是2的N次方;hashMap中的h & (length-1); h=sequence
这里发现了和其它队列不同的的是没有元素的删除操作,只有覆盖操作,数组里的元素对象都是复用的;
无锁机制:
在生产者/消费者模式下,distuptor号称“无锁框架”(BlockingQueue是利用Lock锁做到的),这是怎么做到的那?
一个生产者+一个消费者:
生产者自己维护了一个sequence序号,消费者自己维护了sequence序号,两者各操作各的,不需要加锁;但是需要注意的是两者操作的下标不能大于环形数组一圈,也就是当一个下标中的元素没有被取出时,生产者不能对这个下标位置的元素进行覆盖的,需要进行等待该下标元素被消费者取出之后才能被覆盖,这里的等待策略有很多种,后面介绍;
同样消费者如果消费速度过快,则也需要等待,也是使用四种等待策略,当要读取的下标有元素时,该消费者就会唤醒等待的消费者;
N个消费者+N个生产者
每个消费者都各自维护者自己的sequence序号,多个生产者只维护了一个sequence序号,否则数据就乱套了,这里是通过CAS来保证生产者sequence序号的线程安全,也是没有用到锁;这里生产者的sequence序号也是不能超过消费者最小sequence序号一圈的,就是任意消费者的下标元素还没消费,就不能被生产者覆盖;
等待策略:都是实现了WaitStrategy接口
Disruptor的默认策略是BlockingWaitStrategy。在BlockingWaitStrategy内部是使 用锁和condition来控制线程的唤醒。BlockingWaitStrategy是最低效的策略,但其对CPU 的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现。SleepingWaitStrategy
SleepingWaitStrategy 的性能表现跟 BlockingWaitStrategy 差不多,对 CPU 的消耗也类似,但其对生产者线程的影响最小,通过使用LockSupport.parkNanos(1)来实现循环等待。一般来说Linux系统会暂停一个线程约60µs,这样做的好处是,生产线程不需要采取任何其他行动就可以增加适当的计数器,也不需要花费时间信号通知条件变量。但是,在生产者线程和使用者线程之间移动事件的平均延迟会更高。它在不需要低延迟并且对生产线程的影响较小的情况最好。一个常见的用例是异步日志记录。
YieldingWaitStrategy:YieldingWaitStrategy是可以使用在低延迟系统的策略之一。YieldingWaitStrategy 将自旋以等待序列增加到适当的值。在循环体内,将调用Thread.yield(),以允许其他排队的线程运行。在要求极高性能且事件处理线数小于 CPU 逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。
BusySpinWaitStrategy:性能最好,适合用于低延迟的系统。在要求极高性能且事件处理线程数小于CPU逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性。
下面直接演示disrutor的用法:
/**
* 定义event ; 业务数据对象,生成和消费的业务数据
*/
@Data
public class OrderEvent {
private int id;
}
/**
* 定义业务数据(事件)工厂
*/
public class OrderEventFactory implements EventFactory<OrderEvent> {
@Override
public OrderEvent newInstance() {
return new OrderEvent();
}
}
/**
* 定义消费者(事件处理器)
*/
public class OrderEventHandler implements EventHandler<OrderEvent> {
@Override
public void onEvent(OrderEvent orderEvent, long l, boolean b) throws Exception {
System.out.println(Thread.currentThread().getName()+"消费者:"+orderEvent.getId());
}
}
/**
* 生产者
*/
public class OrderEventProducer {
private final RingBuffer<OrderEvent> ringBuffer;
public OrderEventProducer(RingBuffer<OrderEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void onData(int id) {
// ringBuffer获取下一个 槽位
long sequence = ringBuffer.next();
//获取空的事件,这样就可以复用事件,减少GC;每个槽位对应固定的一个事件
OrderEvent orderEvent = ringBuffer.get(sequence);
//业务数据的封装
orderEvent.setId(id);
//发布事件
ringBuffer.publish(sequence);
System.out.println(Thread.currentThread().getName()+"事件已发布:"+id);
}
}
//Main运行
public class DisruptorDemo {
public static void main(String[] args) throws InterruptedException {
//提供一个线程池,里面的线程作为消费者(Consumer)的事件处理线程
ExecutorService threadPool = Executors.newFixedThreadPool(4);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
//创建事件工厂(业务数据载体工厂)
OrderEventFactory orderEventFactory = new OrderEventFactory();
//创建环形数组的大小,一定是2的指数次幂
int ringBuffferSize = 1024;
//创建Disrutor
Disruptor<OrderEvent> disruptor = new Disruptor<OrderEvent>(orderEventFactory, ringBuffferSize,
threadPool, ProducerType.MULTI, new YieldingWaitStrategy());
//设置事件处理器,也就是消费者,这里设置了两个消费者,一个线程要对应一个消费者,同一个事件被两个消费者消费
EventHandlerGroup<OrderEvent> handlerGroup = disruptor.handleEventsWith(new OrderEventHandler(), new OrderEventHandler());
//这个设置前面两个消费者消费完之后,再消费
//这里可以设置 消费顺序,还有after()定义更灵活的先后顺序
handlerGroup.then(new OrderEventHandler());
//启动disruptor
disruptor.start();
//获取ringBuffer 环形缓冲区,其内部存储的结构是数组
RingBuffer<OrderEvent> ringBuffer = disruptor.getRingBuffer();
//创建自定义的生产者,其实就是传入ringBuffer,然后往里面添加数据
OrderEventProducer eventProducer = new OrderEventProducer(ringBuffer);
//添加数据到生产者中进行发布
for (int i = 1; i < 3; i++) {
eventProducer.onData(i);
}
Thread.sleep(1000);
threadPool.shutdown();
disruptor.shutdown();
}
}
结果为: