Disruptor的设计方案
Disruptor通过以下设计来解决队列速度慢的问题:
- 环形数组结构:
为了避免垃圾回收,采用数组而非链表。同时,数组对处理器的缓存机制更加友好。 - 元素位置定位:
数组长度2^n,通过位运算,加快定位的速度。下标采取递增的形式。不用担心index溢出的问题。index是long类型,即使100万QPS的处理速度,也需要30万年才能用完。 - 无锁设计:
每个生产者或者消费者线程,会先申请可以操作的元素在数组中的位置,申请到之后,直接在该位置写入或者读取数据。
Disruptor的核心概念
RingBuffer
如其名,环形的缓冲区。曾经 RingBuffer 是 Disruptor 中的最主要的对象,但从3.0版本开始,其职责被简化为仅仅负责对通过 Disruptor 进行交换的数据(事件)进行存储和更新。在一些更高级的应用场景中,Ring Buffer 可以由用户的自定义实现来完全替代。
SequenceDisruptor
通过顺序递增的序号来编号管理通过其进行交换的数据(事件),对数据(事件)的处理过程总是沿着序号逐个递增处理。一个 Sequence 用于跟踪标识某个特定的事件处理者( RingBuffer/Consumer )的处理进度。虽然一个 AtomicLong 也可以用于标识进度,但定义 Sequence 来负责该问题还有另一个目的,那就是防止不同的 Sequence 之间的CPU缓存伪共享(Flase Sharing)问题。
Sequencer
Sequencer 是 Disruptor 的真正核心。此接口有两个实现类 SingleProducerSequencer、MultiProducerSequencer ,它们定义在生产者和消费者之间快速、正确地传递数据的并发算法。
Sequence Barrier
用于保持对RingBuffer的 main published Sequence 和Consumer依赖的其它Consumer的 Sequence 的引用。 Sequence Barrier 还定义了决定 Consumer 是否还有可处理的事件的逻辑。
Wait Strategy
定义 Consumer 如何进行等待下一个事件的策略。 (注:Disruptor 定义了多种不同的策略,针对不同的场景,提供了不一样的性能表现)
Event
在 Disruptor 的语义中,生产者和消费者之间进行交换的数据被称为事件(Event)。它不是一个被 Disruptor 定义的特定类型,而是由 Disruptor 的使用者定义并指定。
EventProcessor
EventProcessor 持有特定消费者(Consumer)的 Sequence,并提供用于调用事件处理实现的事件循环(Event Loop)。
EventHandler
Disruptor 定义的事件处理接口,由用户实现,用于处理事件,是 Consumer 的真正实现。
Producer
即生产者,只是泛指调用 Disruptor 发布事件的用户代码,Disruptor 没有定义特定接口或类型。
各概念的作用
- RingBuffer——Disruptor底层数据结构实现,核心类,是线程间交换数据的中转地;
- Sequencer——序号管理器,负责消费者/生产者各自序号、序号栅栏的管理和协调;
- Sequence——序号,声明一个序号,用于跟踪ringbuffer中任务的变化和消费者的消费情况;
- SequenceBarrier——序号栅栏,管理和协调生产者的游标序号和各个消费者的序号,确保生产者不会覆盖消费者未来得及处理的消息,确保存在依赖的消费者之间能够按照正确的顺序处理;
- EventProcessor——事件处理器,监听RingBuffer的事件,并消费可用事件,从RingBuffer读取的事件会交由实际的生产者实现类来消费;它会一直侦听下一个可用的序号,直到该序号对应的事件已经准备好。
- EventHandler——业务处理器,是实际消费者的接口,完成具体的业务逻辑实现,第三方实现该接口;代表着消费者。
- Producer——生产者接口,第三方线程充当该角色,producer向RingBuffer写入事件。
Disruptor实现生产者消费者模式
刚才那个是我们自己使用java.util.concurrent下的类实现的生产者消费者模式,目前业界已经有比较成熟的方案,这里向大家推荐LMAX公司开源的Disruptor框架,Disruptor是一个开源的框架,可以在无锁的情况下对队列进行操作,那么这个队列的设计就是Disruptor的核心所在。
在Disruptor中,采用了RingBuffer来作为队列的数据结构,RingBuffer就是一个环形的数组,既然是数组,我们便可对其设置大小。在这个ringBuffer中,除了数组之外,还有一个序列号,是用来指向数组中的下一个可用元素,供生产者使用或者消费者使用,也就是生产者可以生产的地方,或者消费者可以消费的地方。在Disruptor中使用的是位运算,并且在Disruptor中数组内的元素并不会被删除,而是新数据来覆盖原有数据,所以整个环链的处理效率非常高。
下面我们使用Disruptor来实现刚才用jdk自带库实现的生产者消费者。
Disruptor主要类
-
Disruptor:Disruptor的入口,主要封装了环形队列RingBuffer、消费者集合ConsumerRepository的引用;主要提供了获取环形队列、添加消费者、生产者向RingBuffer中添加事件(可以理解为生产者生产数据)的操作;
-
RingBuffer:Disruptor中队列具体的实现,底层封装了Object[]数组;在初始化时,会使用Event事件对数组进行填充,填充的大小就是bufferSize设置的值;此外,该对象内部还维护了Sequencer(序列生产器)具体的实现;
-
Sequencer:序列生产器,分别有MultiProducerSequencer(多生产者序列生产器) 和 SingleProducerSequencer(单生产者序列生产器)两个实现类。上面的例子中,使用的是SingleProducerSequencer;在Sequencer中,维护了消费者的Sequence(序列对象)和生产者自己的Sequence(序列对象);以及维护了生产者与消费者序列冲突时候的等待策略WaitStrategy;
-
Sequence:序列对象,内部维护了一个long型的value,这个序列指向了RingBuffer中Object[]数组具体的角标。生产者和消费者各自维护自己的Sequence;但都是指向RingBuffer的Object[]数组;
-
Wait Strategy:等待策略。当没有可消费的事件时,消费者根据特定的策略进行等待;当没有可生产的地方时,生产者根据特定的策略进行等待;
-
Event:事件对象,就是我们Ringbuffer中存在的数据,在Disruptor中用Event来定义数据,并不存在Event类,它只是一个定义;
-
EventProcessor:事件处理器,单独在一个线程内执行,判断消费者的序列和生产者序列关系,决定是否调用我们自定义的事件处理器,也就是是否可以进行消费;
-
EventHandler:事件处理器,由用户自定义实现,也就是最终的事件消费者,需要实现EventHandler接口;
-
Producer:事件生产者,也就是我们上面代码中最后那部门的for循环;
待处理类
Disruptor的待处理类和自己实现的待处理类没有本质的区别,可以按照自己要求进行定义。
public class PCData {
private int data;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
待处理类工厂
这里需要实现disruptor的EventFactory接口,并且实现newInstance方法。这里我们实现的newInstance方法,其实就是创建待处理类的对象,该工厂类在创建Disruptor对象的时候会使用到。
import com.lmax.disruptor.EventFactory;
/**
* @Author: feiweiwei
* @Description: 待处理类工厂
* @Created Date: 18:55 17/9/10.
* @Modify by:
*/
public class PCDataFactory implements EventFactory<PCData> {
@Override
public PCData newInstance() {
return new PCData();
}
}
disruptor生产者类
同样需要在生产者中定义一个RingBuffer的环形队列,还需要实现一个push的方法,通过ringBuffer.next()取到下一个待处理类序列号,使用ringBuffer.get(sequence)获取到这个序列号对应的待处理类,并对待处理类进行赋值为新的待处理类。
最后通过ringBuffer.publish(sequence)才会将待处理对象发布出来,消费者才能看到。
import com.lmax.disruptor.RingBuffer;
/**
* @Author: feiweiwei
* @Description: disruptor生产者类
* @Created Date: 18:56 17/9/10.
* @Modify by:
*/
public class Producer {
private final RingBuffer<PCData> ringBuffer;
public Producer(RingBuffer<PCData> ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void pushData(int data){
long sequence = ringBuffer.next();
try{
PCData event = ringBuffer.get(sequence);
event.setData(data);
}finally {
ringBuffer.publish(sequence);
}
}
}
disruptor消费者
disruptor的消费者类需要实现WorkHandler接口,并实现onEvent方法来处理待处理类,例子中只是对待处理类中的值做了平方。
import com.lmax.disruptor.WorkHandler;
/**
* @Author: feiweiwei
* @Description: disruptor消费者
* @Created Date: 18:52 17/9/10.
* @Modify by:
*/
public class Consumer implements WorkHandler<PCData> {
@Override
public void onEvent(PCData pcData) throws Exception {
System.out.println(Thread.currentThread().getId() +
"Event = " + pcData.getData()*pcData.getData());
}
}
Main
待处理类、待处理工厂、生产者、消费者都定义好之后就可以进行使用了,定义一个缓行队列为1024的disruptor对象,这里构造函数入参看名字就知道了,很简单。
PCDataFactory factory = new PCDataFactory();
int bufferSize = 1024;
Disruptor<PCData> disruptor = new Disruptor<PCData>(factory,bufferSize,executor,
ProducerType.MULTI,new BlockingWaitStrategy());
给disruptor对象定义消费者,这里就简单定义两个consumer作为生产者。
disruptor.handleEventsWithWorkerPool(new Consumer(),new Consumer());
初始化Producer并且将ringBuffer作为构造函数入参,并通过生产者循环100次将数据push入队列,消费者会自动从队列取值进行处理。
RingBuffer<PCData> ringBuffer = disruptor.getRingBuffer();
Producer producer = new Producer(ringBuffer);
for(int i=0; i<100; i++){
producer.pushData(i);
Thread.sleep(100);
System.out.println("push data " + i);
}
以下为Main全部代码:
package com.monkey01.producercustomer.disruptor;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* @Author: feiweiwei
* @Description:
* @Created Date: 18:59 17/9/10.
* @Modify by:
*/
public class Main {
public static void main(String args[]) throws InterruptedException {
Executor executor = Executors.newCachedThreadPool();
PCDataFactory factory = new PCDataFactory();
int bufferSize = 1024;
Disruptor<PCData> disruptor = new Disruptor<PCData>(factory,bufferSize,executor,
ProducerType.MULTI,new BlockingWaitStrategy());
disruptor.handleEventsWithWorkerPool(new Consumer(),
new Consumer());
disruptor.start();
RingBuffer<PCData> ringBuffer = disruptor.getRingBuffer();
Producer producer = new Producer(ringBuffer);
for(int i=0; i<100; i++){
producer.pushData(i);
Thread.sleep(100);
System.out.println("push data " + i);
}
disruptor.shutdown();
}
}
作者:monkey01
链接:www.jianshu.com/p/c67f74745…
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:codeobj
链接:juejin.cn/post/684490…
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。