最近项目中有用到disruptor,提供一个类似队列或者数据容器的功能,并发能力很强
概念:
Sequence:就是一个增长序列,类似oracle的增长序列,生产和消费程序都有Sequence,记录生产和消费程序的序列
Sequencer: 多个概念的一个组合,持有Sequence,等待策略等一些引用,生产者引用
SequenceBarrier:直接翻译就是序列屏障,就是Sequence和RingBuffer交互的一个屏障,单个生产者时,生产者不需要SequenceBarrier
RingBuffer:是一个数组,根据追踪生产和消费的Sequence来实现一个环形的数据结构
说明:
1 很多地方都说到RingBuffer是一个环形的数据结构,它功能上表现出来的确实是环形结构,但是实现上是一个数组,而是通过生产者覆盖已经读过的数据,消费者回头读取未读取过的数据来实现的环形数据结构,
很多画图画成环形的,很容易误导理解
2 RingBuffer的size为2的n次方,可是Sequence是一直递增的,不知道其他人怎么理解的,我原来没看代码前的理解就是它的长度不超过RingBuffer的长度,然后重置后重新增长,这个错误的理解主要是看了很多博客上的环形的那个图,不论生产还是消费的Sequence一直
都是递增的, 到RingBuffer取值时,会根据RingBuffer的长度转换成对应的下标值
1 maven pom
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.0</version>
</dependency>
2 我现在只是用到,了解的不是很深,看了部分代码,参考了很多其他博客,下面是我自己的看法,不对的地方欢迎指正
2.1 测试代码是这样用的
TestEvent.java
public class TestEvent {
private String line;
public String getLine() {
return line;
}
public void setLine(String line) {
this.line = line;
}
}
生产者TestEventProducer.java:
public class TestEventProducer {
private RingBuffer<TestEvent> ringBuffer;
public TestEventProducer (RingBuffer<TestEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
}
/**
* 转换器,向队列里放值的时候调用(队列先设置固定长度的对象,然后通过set方法生产值)
*/
private static EventTranslatorOneArg<TestEvent, String> eventTranslatorOneArg = new EventTranslatorOneArg<TestEvent, String>() {
@Override
public void translateTo(TestEvent event, long sequence, String arg0) {
event.setLine(arg0);
}
};
/**
* 生产者向队列里放入数据
* @param line
*/
public void onData (String line) {
this.ringBuffer.publishEvent(eventTranslatorOneArg, line);
}
/**
* 处理数据
*/
public void handler () {
for (int i = 0; i < 1000; i++) {
this.onData("wozaizhe" + i);
}
}
消费者TestEventHandler:消费者从RingBuffer中拿出数据打印了一下
public class TestEventHandler implements WorkHandler<TestEvent> {
@Override
public void onEvent(TestEvent event) throws Exception {
System.out.println("处理了一行数据:" + event.getLine());
}
}
测试类:
public class TestEventMain {
public static final int BUFFER_SIZE = 8;
public static void main (String[] args) {
testDisruptor();
}
public static void testDisruptor () {
ExecutorService executor = Executors.newFixedThreadPool(8);
EventFactory<TestEvent> eventFactory = new TestEventFactory();
//创建disruptor,设置单生产者模式
Disruptor disruptor = new Disruptor(eventFactory, BUFFER_SIZE, executor, ProducerType.SINGLE,
new YieldingWaitStrategy ());
//设置消费者程序
disruptor.handleEventsWithWorkerPool(new TestEventHandler(), new TestEventHandler(),
new TestEventHandler(), new TestEventHandler());
//设置异常处理
disruptor.handleExceptionsWith(new TestEventExceptionHandler());
//启动disruptor并返回RingBuffer
RingBuffer<TestEvent> ringBuffer = disruptor.start();
//创建生产者线程,并生产
TestEventProducer producer = new TestEventProducer(ringBuffer);
producer.handler();
disruptor.shutdown();
executor.shutdown();
}
}
测试中使用的是单一的生产者,消费者有多个,使用WorkerPool来管理多个消费者
disruptor 的数据存放在RingBuffer中,RingBuffer的类结构:
disruptor设计的主要类
上面是主要类图,RingBuffer只负责存储,生产者和消费者取号的协调工作都是由SingleProducerSequencer来完成的
从代码分析:
(1) 创建Disruptor
public Disruptor(final EventFactory<T> eventFactory,
final int ringBufferSize,
final Executor executor,
final ProducerType producerType,
final WaitStrategy waitStrategy)
{
this(RingBuffer.create(producerType, eventFactory, ringBufferSize, waitStrategy),
executor);
}
//创建RingBuffer
public static <E> RingBuffer<E> create(ProducerType producerType,
EventFactory<E> factory,
int bufferSize,
WaitStrategy waitStrategy)
{
switch (producerType)
{
case SINGLE:
return createSingleProducer(factory, bufferSize, waitStrategy);
case MULTI:
return createMultiProducer(factory, bufferSize, waitStrategy);
default:
throw new IllegalStateException(producerType.toString());
}
}
//创建Sequencer并实例化到RingBuffer中
public static <E> RingBuffer<E> createSingleProducer(EventFactory<E> factory,
int bufferSize,
WaitStrategy waitStrategy)
{
SingleProducerSequencer sequencer = new SingleProducerSequencer(bufferSize, waitStrategy);
return new RingBuffer<E>(factory, sequencer);
}
创建Disruptor的过程: 创建了一个RingBuffer,实例化了生产者sequencer,实例化了其他参数
(2) 设置消费者程序
源码:
//被调用的方法
public EventHandlerGroup<T> handleEventsWithWorkerPool(final WorkHandler<T>... workHandlers)
{
return createWorkerPool(new Sequence[0], workHandlers);
}
//创建workpool
EventHandlerGroup<T> createWorkerPool(final Sequence[] barrierSequences, final WorkHandler<? super T>[] workHandlers)
{
//创建SequenceBarrier,每次消费者要读取RingBuffer中的下一个值都要通过SequenceBarrier来获取SequenceBarrier用来协调多个消费者并发的问题
final SequenceBarrier sequenceBarrier = ringBuffer.newBarrier(barrierSequences);
//实现在下个方法
final WorkerPool<T> workerPool = new WorkerPool<T>(ringBuffer, sequenceBarrier, exceptionHandler, workHandlers);
consumerRepository.add(workerPool, sequenceBarrier);
return new EventHandlerGroup<T>(this, consumerRepository, workerPool.getWorkerSequences());
}
//workpool 构造方法
public WorkerPool(final RingBuffer<T> ringBuffer,
final SequenceBarrier sequenceBarrier,
final ExceptionHandler exceptionHandler,
final WorkHandler<? super T>... workHandlers)
{
this.ringBuffer = ringBuffer;
final int numWorkers = workHandlers.length;
//创建一个和消费者线程个数相同的WorkProcessor数组
workProcessors = new WorkProcessor[numWorkers];
for (int i = 0; i < numWorkers; i++)
{
//消费者在源码中的表现形式就是WorkProcessor,通过构造方法可以知道,一个workpool中的消费者程序,使用的是相同的sequenceBarrier, workSequence
workProcessors[i] = new WorkProcessor<T>(ringBuffer,
sequenceBarrier,
workHandlers[i],
exceptionHandler,
workSequence);
}
}
这个部分就是创建一个workpool,根据每个WorkHandler创建对应的WorkProcessor,同一个workpool中的消费者线程共享同一个sequenceBarrier,workSequence,类的关系可以看上面的类图
(3) 启动disruptor
public RingBuffer<T> start()
{
//获取每一个消费者程序的Sequence和workpool的Sequence
Sequence[] gatingSequences = consumerRepository.getLastSequenceInChain(true);
ringBuffer.addGatingSequences(gatingSequences);
checkOnlyStartedOnce();
for (ConsumerInfo consumerInfo : consumerRepository)
{
consumerInfo.start(executor);
}
return ringBuffer;
}
//根据一系列的引用,找到消费者程序WorkProcessor,初始化每个WorkProcessor的sequence,然后执行提交到线程池执行
public RingBuffer<T> start(final Executor executor)
{
if (!started.compareAndSet(false, true))
{
throw new IllegalStateException("WorkerPool has already been started and cannot be restarted until halted.");
}
final long cursor = ringBuffer.getCursor();
workSequence.set(cursor);
for (WorkProcessor<?> processor : workProcessors)
{
processor.getSequence().set(cursor);
executor.execute(processor);
}
return ringBuffer;
}
启动Disruptor就是启动消费者线程
(4)单一生产者生产数据
@Override
public <A> void publishEvent(EventTranslatorOneArg<E, A> translator, A arg0)
{
//获取RingBuffer下一个可操作的序列
final long sequence = sequencer.next();
//把数据set到队列,设置cursor的序列
translateAndPublish(translator, sequence, arg0);
}
/**
* @see Sequencer#next()
*/
@Override
public long next()
{
return next(1);
}
/**
* @see Sequencer#next(int)
*/
@Override
public long next(int n)
{
if (n < 1)
{
throw new IllegalArgumentException("n must be > 0");
}
//RingBuffer持有Sequencer,SingleProducerSequencer有nextValue和cachedValue两个成员变量,前者记录生产者生产到的位置,后者记录消费者线程中序列号最小的序列号,即是在最前面的消费者的序号
long nextValue = this.nextValue;
long nextSequence = nextValue + n;
//wrapPoint是一个很关键的变量,这个变量决定生产者是否可以覆盖序列号nextSequence,wrapPoint是为什么是nextSequence - bufferSize;RingBuffer表现出来的是一个环形的数据结构,实际上是一个长度为bufferSize的数组,
//nextSequence - bufferSize如果nextSequence小于bufferSize wrapPoint是负数,表示可以一直生产;如果nextSequence大于bufferSize wrapPoint是一个大于0的数,由于生产者和消费者的序列号差距不能超过bufferSize
//(超过bufferSize会覆盖消费者未消费的数据),wrapPoint要小于等于多个消费者线程中消费的最小的序列号,即cachedValue的值,这就是下面if判断的根据
long wrapPoint = nextSequence - bufferSize;
long cachedGatingSequence = this.cachedValue;
//继续生成会覆盖消费者未消费的数据
if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
{
long minSequence;
//判断wrapPoint是否大于消费者线程最小的序列号,如果大于,不能写入,继续等待
while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue)))
{
LockSupport.parkNanos(1L); // TODO: Use waitStrategy to spin?
}
//满足生产条件了,缓存这次消费者线程最小消费序号,供下次使用
this.cachedValue = minSequence;
}
//缓存生产者最大生产序列号
this.nextValue = nextSequence;
return nextSequence;
}
(5) 消费者程序WorkProccessor
//线程的逻辑
@Override
public void run()
{
if (!running.compareAndSet(false, true))
{
throw new IllegalStateException("Thread is already running");
}
sequenceBarrier.clearAlert();
notifyStart();
//标志位,用来标志一次消费过程,此标志位在代码方面用的很巧妙,把两次执行揉成一段代码
boolean processedSequence = true;
//用来缓存消费者可以使用的RingBuffer最大序列号
long cachedAvailableSequence = Long.MIN_VALUE;
//记录被分配的WorkSequence的序列号,也是去RingBuffer取数据的序列号
long nextSequence = sequence.get();
T event = null;
while (true)
{
try
{
// if previous sequence was processed - fetch the next sequence and set
// that we have successfully processed the previous sequence
// typically, this will be true
// this prevents the sequence getting too far forward if an exception
// is thrown from the WorkHandler
//每次消费开始执行
if (processedSequence)
{
processedSequence = false;
//使用CAS算法从WorkPool的序列WorkSequence取得可用序列nextSequence
do
{
nextSequence = workSequence.get() + 1L;
sequence.set(nextSequence - 1L);
}
while (!workSequence.compareAndSet(nextSequence - 1L, nextSequence));
}
//如果可使用的最大序列号cachedAvaliableSequence大于等于我们要使用的序列号nextSequence,直接从RingBuffer取数据;不然进入else
if (cachedAvailableSequence >= nextSequence)
{
//可以满足消费的条件,根据序列号去RingBuffer去读取数据
event = ringBuffer.get(nextSequence);
workHandler.onEvent(event);
//一次消费结束,设置标志位
processedSequence = true;
}
else
{//等待生产者生产,获取到最大的可以使用的序列号
cachedAvailableSequence = sequenceBarrier.waitFor(nextSequence);
}
}
catch (final AlertException ex)
{
if (!running.get())
{
break;
}
}
catch (final Throwable ex)
{
// handle, mark as processed, unless the exception handler threw an exception
exceptionHandler.handleEventException(ex, nextSequence, event);
processedSequence = true;
}
}
notifyShutdown();
running.set(false);
}
//ProcessorSequencerBarrier.java等待生产者生产出更多的产品用来消费
@Override
public long waitFor(final long sequence)
throws AlertException, InterruptedException, TimeoutException
{
checkAlert();
//根据选用的等待策略来等待生产者生产
long availableSequence = waitStrategy.waitFor(sequence, cursorSequence, dependentSequence, this);
//这个不明白是为什么,生产者最大的序列小于要使用的序列,直接返回了,上面的run()方法中的while循环要再执行一遍,不明白此处的用意
if (availableSequence < sequence)
{
return availableSequence;
}
//返回较大的Sequence
return sequencer.getHighestPublishedSequence(sequence, availableSequence);
}
//YieldingWaitStrategy 等待策略,先尝试一百次,再不满足条件,当前线程就yield,让其他线程先执行
@Override
public long waitFor(
final long sequence, Sequence cursor, final Sequence dependentSequence, final SequenceBarrier barrier)
throws AlertException, InterruptedException
{
long availableSequence;
//等待次数
int counter = SPIN_TRIES;
//循环,如果生产的最大序列号小于消费者需要的序列号,继续等待,等待次数超过counter次,线程yield
//这里dependentSequence就是cursorSequence,在ProcessorSequencerBarrier构造函数中可以看到
while ((availableSequence = dependentSequence.get()) < sequence)
{
counter = applyWaitMethod(barrier, counter);
}
return availableSequence;
}
//counter大于0则减一返回,否则当前线程yield
private int applyWaitMethod(final SequenceBarrier barrier, int counter)
throws AlertException
{
barrier.checkAlert();
if (0 == counter)
{
Thread.yield();
}
else
{
--counter;
}
return counter;
}
消费者的整体逻辑:多个消费者共同使用同一个Sequence即workSequence,大家都从这个sequence里取得序列号,通过CAS保证线程安全,然后每个消费者拿到序列号nextSequence后去和RingBuffer的cursor比较,即生产者生产到的最大序列号比较,如果自己要取的序号还没有被生产者生产出来,则等待生产者生成出来后再从RingBuffer中取数据,处理数据
上面的每个线程run()方法是while(true)怎么停下来的呢,通过跑出异常来控制的,抛出AlertException,在捕捉到异常后break循环语句块
促使抛出异常在关闭disruptor的时候
(6)disruptor关闭
public void shutdown(final long timeout, final TimeUnit timeUnit) throws TimeoutException
{
final long timeOutAt = System.currentTimeMillis() + timeUnit.toMillis(timeout);
//断是否有剩余消息未发送,有则继续循环
while (hasBacklog())
{
if (timeout >= 0 && System.currentTimeMillis() > timeOutAt)
{
throw TimeoutException.INSTANCE;
}
// Busy spin
}
//数据全部发送和消费完毕
halt();
}
/**
* Confirms if all messages have been consumed by all event processors
*/
private boolean hasBacklog()
{
final long cursor = ringBuffer.getCursor();
for (final Sequence consumer : consumerRepository.getLastSequenceInChain(false))
{
//通过判断生产数是否等于消费数,等于表示生产消费结束,返回false
if (cursor > consumer.get())
{
return true;
}
}
return false;
}
//线程运行设置为false, Barrier的alert设置为true,run()方法执行while循环的时候会检查一下alert,为true则跳出循环
@Override
public void halt()
{
running.set(false);
sequenceBarrier.alert();
}
总体来说:RingBuffer在生产Sequencer中记录一个cursor,追踪生产者生产到的最新位置,通过WorkSequence和sequence记录整个workpool消费的位置和每个WorkProcessor消费到位置,来协调生产和消费程序