disruptor的使用和分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/casuallc/article/details/51166352

disruptor:3.3.4
jdk:1.8

介绍:

disruptor是典型的生产者和消费者模式,disruptor使用RingBuffer存放数据,sequence管理生产和消费的位置(long类型,递增),生产者要生产数据的时候首先向生产者请求一个sequence,然后在sequence的位置放置数据。

入门:

生产的数据模型

public class LogEvent {

    private long value;

    public void setValue(long value) {
        this.value = value;
    }

    public String toString() {
        return this.value + "";
    }
}

消费者消费数据

public class EventHandler {

    public void handleEvent(LogEvent event, long sequence, boolean falg) throws Exception {
        System.out.println("handle event " + event + " cur thread " + Thread.currentThread().getId());
    }
}

初始化:

void sigleProducerSingleConsume() throws Exception {
        int bufferSize = 8; // RingBuffer的大小,2的n次方
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        EventHandler handler = new EventHandler();
        Disruptor<LogEvent> disruptor = new Disruptor<>(LogEvent::new, bufferSize, threadFactory, ProducerType.SINGLE, new BlockingWaitStrategy());
        disruptor.handleEventsWith(handler::handleEvent);
        RingBuffer<LogEvent> buffer = disruptor.start();
        for(int i=0; i<10; i++) {
            long sequence = buffer.next(); // 申请位置
            try {
                LogEvent event = buffer.get(sequence);
                event.setValue(i); // 放置数据
            } finally {
                buffer.publish(sequence); // 提交,如果不提交完成事件会一直阻塞
            }
        }
        disruptor.shutdown();
    }

原理解析:

多个生产者模式:
Disruptor<LogEvent> disruptor = new Disruptor<>(LogEvent::new, bufferSize, threadFactory, ProducerType.MULTI, new BlockingWaitStrategy());

初始化方法:

 public Disruptor(
            final EventFactory<T> eventFactory,
            final int ringBufferSize,
            final ThreadFactory threadFactory,
            final ProducerType producerType,
            final WaitStrategy waitStrategy)
    {
        this(RingBuffer.create(
                               producerType, eventFactory, ringBufferSize, waitStrategy),
                new BasicExecutor(threadFactory));
    }

创建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());
        }
    }
public static <E> RingBuffer<E> createMultiProducer(
        EventFactory<E> factory,
        int bufferSize,
        WaitStrategy waitStrategy)
    {
        MultiProducerSequencer sequencer = new MultiProducerSequencer(bufferSize, waitStrategy);

        return new RingBuffer<E>(factory, sequencer);
    }

这里可以看到,使用MultiProducerSequencer 创建了RingBuffer,当我们使用单个生产者模式的时候可以看到使用SingleProducerSequencer创建了RingBuffer,所以,disruptor是使用一个sequence来维护生产者用到的index 的。

让我们看看MultiProducerSequencer 的代码:

public MultiProducerSequencer(int bufferSize, final WaitStrategy waitStrategy)
    {
        super(bufferSize, waitStrategy);
        availableBuffer = new int[bufferSize];
        indexMask = bufferSize - 1;
        indexShift = Util.log2(bufferSize);
        initialiseAvailableBuffer();
    }

要注意availableBuffer ,这个数组中保存了生产者在每个位置添加数据的信息。
indexShift :bufferSize >> indexShift + 1 时为0
initialiseAvailableBuffer()初始化availableBuffer 所有元素为-1

然后让我们看添加数据的处理:

long sequence = buffer.next(); // 申请位置
try {
    LogEvent event = buffer.get(sequence);
    event.setValue(i); // 放置数据
} finally {
    buffer.publish(sequence); // 提交,如果不提交完成事件会一直阻塞
}

取得可用的位置

@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");
        }

        long current;
        long next;

        do
        {
            current = cursor.get(); // 
            next = current + n; // 获取下一个可用位置,最好不要用next(n)来获取位置,很容易产生死循环

            long wrapPoint = next - bufferSize; // 
            long cachedGatingSequence = gatingSequenceCache.get(); // 保存了消费者处理过的最小位置
        // 保证生产者没有超过消费者一圈。
            if (wrapPoint > cachedGatingSequence || cachedGatingSequence > current)
            {
                long gatingSequence = Util.getMinimumSequence(gatingSequences, current);

                if (wrapPoint > gatingSequence)
                {
                    LockSupport.parkNanos(1); // TODO, should we spin based on the wait strategy?
                    continue;
                }

                gatingSequenceCache.set(gatingSequence);
            }
            else if (cursor.compareAndSet(current, next))
            {
                break;
            }
        }
        while (true);

        return next;
    }

中心思想就是获取当前可用的位置,这里没有用到锁,而是使用了cas。
gatingSequence中保存了各个消费者处理过的当前位置。

这样当前生产者就可以使用这个位置了,最后要记得publish,告知消费者去读取数据。

    @Override
    public void publish(final long sequence)
    {
        setAvailable(sequence); // 
        waitStrategy.signalAllWhenBlocking(); // 消费者可以读了
    }

setAvailable是一个很巧妙的设计,如果是多个生产者,availableBuffer中的值是这样变化的:
[-1, -1, -1, -1]
[0, -1, -1, -1]
[0, 0, 0, 0]
至此,四个位置已经被写入数据了
[1, 0, 0, 0]
[1, 1, 0, 0]
[1, 1, 0, 1]
[1, 1, 1, 1]
看第三种情形,producer1占据了第三个位置,但是producer2先一步在第四个位置写入了数据,consumer是可以跳过第三个位置读取第四个位置的数据的。但是这种情况会极少出现,第三个位置没有没consumer读取的话,producer2是不能拿到第四个位置的。

private void setAvailable(final long sequence)
    {
        setAvailableBufferValue(calculateIndex(sequence), calculateAvailabilityFlag(sequence));
    }
  private int calculateAvailabilityFlag(final long sequence)
    {
        return (int) (sequence >>> indexShift);
    }

    private int calculateIndex(final long sequence)
    {
        return ((int) sequence) & indexMask;
    }
 private void setAvailableBufferValue(int index, int flag)
    {
        long bufferAddress = (index * SCALE) + BASE;
        UNSAFE.putOrderedInt(availableBuffer, bufferAddress, flag);
    }
展开阅读全文

没有更多推荐了,返回首页