Disruptor高速队列使用

一、基本概念

环形无锁高速队列。
  这里的无锁是指:不使用锁来保证原子性,而通过使用CAS保证原子性。

数组下标和Sequence的关系:
  假设我创建了一个长度大小为8的环形数组,这意味着该数组下标只能是从0-7。而序列号Sequence可以认为是当前生产者已经生产到了第几个产品了。Sequence %数组长度 = Sequence对应的数组下标。
  每生产一个,序列号就会自增,序列号的大小是2的64次幂-1,因为它是long类型的。

如何保证生产者不会把为消费的数据给覆盖了?(假设ringBufferSize=8)
  要知道我们的数组是可以复用的,因为我们的数组是环形的(逻辑上),例如 生产者从下标为0生产到下标为7的位置,而消费者也将下标为0-4的数据给消费掉了,那么生产者就又可以重头开始继续生产,将新生产的数据从下标0放到4,覆盖掉旧数据。即边消费边生产的过程。
  回归正传,如何防止生产者把未消费的数据给覆盖了?假如当前消费者消费到了sequence为10的位置(此时sequence10的位置也已经消费掉了,而sequence11的位置还没有被消费),而生产者即将生产的sequence值为18,此时就会进行18-10(生产者的sequence-ringBufferSize)=10,判断是10 > 10,发现不成立,那么生产者可以生产(因为sequence10的位置已经被消费了)。然后生产者会继续生产,此时即将生产的sequence为19,此时19-8=11 > 10,此时生产者就不能生产了(因为11的这个位置还没有被消费)。
  上面是单消费者的情况,对于多消费者也是类似。如果是多消费者,那么上面的消费者seqence就是指众多消费者中senquence最小的那个。

上面为什么要 (生产者的Seqence - RingBufferSize)
  因为生产者和消费者的seqence之差不会超过一圈,也就是不会超过ringBufferSize,毕竟这是一个环形数组。相差最大的时候就是 消费者没有消费,而生产者生产了一圈。如果相差超过ringBufferSize,那么这个时候就会出现覆盖未消费的数据。

下面是获取下一个sequence号的源码

public long next() {
    return this.next(1);
}
public long next(int n) {
    if (n < 1) {
        throw new IllegalArgumentException("n must be > 0");
    } else {
        long nextValue = this.nextValue;
        long nextSequence = nextValue + (long)n;
        long wrapPoint = nextSequence - (long)this.bufferSize;
        long cachedGatingSequence = this.cachedValue;
        if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue) {
            this.cursor.setVolatile(nextValue);

            long minSequence;
            while(wrapPoint > (minSequence = Util.getMinimumSequence(this.gatingSequences, nextValue))) {
                LockSupport.parkNanos(1L);
            }

            this.cachedValue = minSequence;
        }

        this.nextValue = nextSequence;
        return nextSequence;
    }
}

Disruptor的优点:

  1、无锁,利用CAS进行处理,而无需加锁,提高了速度。(相对比jdk自带的xxxBlockedQueue快)
  2、可以复用数组,提高JVM的内存利用率,减少GC次数,同时可以防止应生产者过快生产,而导致内存不足的风险。(djk也有利用CAS的无锁队列,但是存在生产者生产过快而导致内存不足的风险)

二、单生产者单消费者DEMO

<!-- https://mvnrepository.com/artifact/com.lmax/disruptor -->
    <dependency>
      <groupId>com.lmax</groupId>
      <artifactId>disruptor</artifactId>
      <version>3.4.2</version>
    </dependency>

1、生产者

package com.jxf.disruptor.single;

import com.lmax.disruptor.RingBuffer;

public class StringEventProducer {

    private RingBuffer<StringEvent> ringBuffer;

    public StringEventProducer(
        RingBuffer<StringEvent> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }

    /**
     * 生产数据
     */
    public void setData(String data) {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 1、获取待生产下一个的序号
        long sequeuence = ringBuffer.next();
        try {
            // 2、获取下一个待生产的 数据(空)
            StringEvent stringEvent = ringBuffer.get(sequeuence);
            // 3、填充产品数据
            stringEvent.setValue(data);
            System.out.println("生产-->" + data);
        } finally {
            // 4、发布产品。消费者可以去消费。
            ringBuffer.publish(sequeuence);
        }

    }
}

2、生产者生产的数据对象(在队列中传输的对象)

package com.jxf.disruptor.single;

import lombok.Data;

@Data
/**
 * 放在队列中的对象(数据)
 */
public class StringEvent {

    private String value;

}

3、消费者

package com.jxf.disruptor.single;

import com.lmax.disruptor.EventHandler;

/**
 * 消费者
 */
public class StringEventHandler implements EventHandler<StringEvent> {

    /**
     * 消费数据的方法
     *
     * @param stringEvent 被消费的数据
     * @param sqeueuence 消费序号 从0 到 2的64次幂-1(long类型的大小)
     * @param endOfBatch 当前是否已经消费到尽头了(此刻没有下一个需要消费的数据)
     */
    @Override
    public void onEvent(StringEvent stringEvent, long sqeueuence, boolean endOfBatch) {
        System.out.println(
            "消费 -->" + stringEvent.getValue() + " sqeueuence-->" + +sqeueuence + "  endOfBatch-->"
                + endOfBatch);
    }
}

4、测试

package com.jxf.disruptor.single;

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class MainTest {

    public static void main(String[] args) {
        // 创建一个线程工厂提供线程来触发Consumer的事件处理
        ThreadFactory threadFactory = Executors.defaultThreadFactory();

        // 环形数组大小
        int ringBufferSize = 16;

        // 第一个参数是事件工厂。用于生产事件(数据)
        Disruptor<StringEvent> disruptor = new Disruptor<>(() -> new StringEvent(), ringBufferSize,
            threadFactory, ProducerType.SINGLE, new YieldingWaitStrategy());

        // 连接消费者方法
        disruptor.handleEventsWith(new StringEventHandler());

        // 启动消费者
        disruptor.start();

        RingBuffer ringBuffer = disruptor.getRingBuffer();
        // 创建生产者
        StringEventProducer eventProducer = new StringEventProducer(ringBuffer);
        for (int i = 1; i <= 1000; i++) {
            // 生产数据
            eventProducer.setData("数据" + i);
        }
        disruptor.shutdown();
    }
}

三、多生产者多消费者(竞争关系-非重复消费)DEMO

  即队列中的一个数据只会被其中的一个消费者去消费。worker模式,即竞争模式

1、队列传输对象

package com.jxf.disruptor.multi;

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Order {

    private String id;

}


2、生产者

package com.jxf.disruptor.multi;

import com.lmax.disruptor.RingBuffer;

public class OrderProducer {

    private RingBuffer<Order> ringBuffer;

    public OrderProducer(
        RingBuffer<Order> ringBuffer) {
        this.ringBuffer = ringBuffer;
    }

    /**
     * 生产数据
     */
    public void setData(String data) {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 1、获取待生产下一个的序号
        long sequeuence = ringBuffer.next();
        try {
            // 2、获取下一个待生产的 数据(空)
            Order order = ringBuffer.get(sequeuence);
            // 3、填充产品数据
            order.setId(data);
            System.out.println("生产者 生产-->" + order);
        } finally {
            // 4、发布产品。消费者可以去消费。
            ringBuffer.publish(sequeuence);
        }

    }
}

3、消费者

package com.jxf.disruptor.multi;

import com.lmax.disruptor.WorkHandler;

/**
 * 消费者
 */
public class OrderWorkHandler implements WorkHandler<Order> {

    // 消费者编号
    private int id;

    public OrderWorkHandler(int id) {
        this.id = id;
    }

    @Override
    public void onEvent(Order order) throws Exception {
        System.out.println("消费者" + id + " 消费-->" + order);
    }
}

4、队列的异常处理
package com.jxf.disruptor.multi;

import com.lmax.disruptor.ExceptionHandler;

/**
 * 队列异常时的处理
 */
public class OrderExceptionHandler implements ExceptionHandler<Order> {

    @Override
    public void handleEventException(Throwable throwable, long l, Order order) {

    }

    @Override
    public void handleOnStartException(Throwable throwable) {

    }

    @Override
    public void handleOnShutdownException(Throwable throwable) {

    }
}

5、整合测试

package com.jxf.disruptor.multi;

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.WorkerPool;
import com.lmax.disruptor.YieldingWaitStrategy;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainTest {

    public static void main(String[] args) {
        // 环形数组大小
        int ringBufferSize = 1024 * 1024;

        // 创建一个线程工厂提供线程来触发Consumer的事件处理
        RingBuffer<Order> ringBuffer = RingBuffer
            .createMultiProducer(() -> new Order(), ringBufferSize, new YieldingWaitStrategy());

        // 通过ringBuffer创建一个屏障
        SequenceBarrier barrier = ringBuffer.newBarrier();

        // 创建多个消费者
        OrderWorkHandler[] workHandlers = new OrderWorkHandler[16];
        for (int i = 0; i < workHandlers.length; i++) {
            workHandlers[i] = new OrderWorkHandler(i);
        }

        // 创建工作池
        WorkerPool<Order> workerPool = new WorkerPool<>(ringBuffer, barrier,
            new OrderExceptionHandler(), workHandlers);

        // 创建消费者线程池(创建16个线程的线程池,每个消费者可以对应一个线程)
        ExecutorService threadPool = Executors.newFixedThreadPool(workHandlers.length);

        // 启动工作池(此时我们的队列就在允许中了...消费者也在等待数据消费)
        workerPool.start(threadPool);

        // 模拟100个生产者,每个生产者生产100条消息
        for (int i = 1; i <= 100; i++) {
            OrderProducer orderProducer = new OrderProducer(ringBuffer);
            for (int j = 1; j <= 100; j++) {
                orderProducer.setData(UUID.randomUUID().toString());
            }
        }
    }
}

四、多生产者多消费者(独立关系-重复消费)DEMO

  说明:和2的区别就是,2的每个消费者不会重复消费队列中的数据。而这里的3的消费者是独立关系,会重复消费队列中的数据。(应该结合应用场景选择2或者3)
代码和2差不多

变动一:消费者实现EventHandler接口。(之前的wokerHandler是woker模式,是竞争模式)

package com.jxf.disruptor.multi;

import com.lmax.disruptor.EventHandler;

/**
 * 消费者
 */
public class OrderEventHandler implements EventHandler<Order> {

    // 消费者编号
    private int id;

    public OrderEventHandler(int id) {
        this.id = id;
    }

    @Override
    public void onEvent(Order order, long sqeueuence, boolean endOfBatch) throws Exception {
        System.out.println(
            "消费者" + id + "消费 -->" + order.getId() + " sqeueuence-->" + +sqeueuence
                + "  endOfBatch-->"
                + endOfBatch);
    }
}

变动二:Main方法

package com.jxf.disruptor.multi;

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.WorkerPool;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

public class MainTest2 {

    public static void main(String[] args) {
        // 环形数组大小
        int ringBufferSize = 1024 * 1024;

        // 创建多个消费者
        OrderEventHandler[] eventHandlers = new OrderEventHandler[16];
        for (int i = 0; i < eventHandlers.length; i++) {
            eventHandlers[i] = new OrderEventHandler(i);
        }

        Disruptor<Order> disruptor = new Disruptor<>(() -> new Order(), ringBufferSize,
            Executors.defaultThreadFactory(), ProducerType.MULTI, new YieldingWaitStrategy());
        disruptor.setDefaultExceptionHandler(new OrderExceptionHandler());
        // 异常处理
        disruptor.handleEventsWith(eventHandlers);
        //  启动队列(启动消费者)
        disruptor.start();

        RingBuffer<Order> ringBuffer = disruptor.getRingBuffer();

        // 模拟100个生产者,每个生产者生产100条消息
        for (int i = 1; i <= 5; i++) {
            OrderProducer orderProducer = new OrderProducer(ringBuffer);
            for (int j = 1; j <= 1; j++) {
                orderProducer.setData(UUID.randomUUID().toString());
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值