Disruptor简介及其应用

Disruptor

背景

Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。

目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能。

参考文档

应用场景

我之前遇到一个问题场景:前端发送一个数据,通过web应用异步调用外部引擎,外部引擎对其进行相关计算操作后,外部引擎回调web应用的接口,将结果响应给web应用,web应用会将结果持久化到数据库中。

当少量请求发送时,此应用并没有问题,一旦做大量请求(批量)时,引擎运行十分缓慢。后由Jconsole查看JVM的内存情况,发现是: 1. 并发太高导致线程推进速度缓慢 2. 内存占用太多引起的频繁GC

每次引擎回调动作都需要启动一个线程对数据库进行插入操作,并且等待数据库返回结果,当大量回调请求涌入,线程将会瞬间大量创建。

内存占用问题是,回调的结果数据量很大,并且在插入数据库之后就不再使用该数据了,其也会发生问题。

解决方案1

解决并发太高导致线程推进速度缓慢问题

在回调接口处使用线程池进行对数据库持久化操作进行异步处理。

  • 优点:并发度可控
  • 缺点:
    • 对象的频繁产生与回收
    • 线程池底层BlockingQueueDisruptor有锁效率问题

解决方案2

使用Disruptor队列解决

使用多生产者多消费者模式进行,多生产者为引擎的多线程回调,多消费者为,我们使用一个WorkPool使用多个相同逻辑的Handler进行对事件的消费,实现多线程消费。

  • 优点:
    • 并发度可控
    • 由于Disruptor的底层实现,对象的频繁产生与回收问题得到一定的解决(初始化一篇区域用于存放对象)
    • Disuptor效率更加高:
      1. 伪共享问题
      2. Disruptor是无锁的队列,效率更高
      3. 底层为数组的环形链表,顺序寻址,速度更快(与ConcurrentLinkedQueue比较,其为链表实现)
    • Disruptor使用了事件监听设计模式,易于拓展新的消费者(业务逻辑),与易于组合复杂的业务逻辑
  • 缺点:引入了外部依赖

思维导图

用一个小Demo模拟一下场景

package pro.eddievim;

import com.lmax.disruptor.*;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;

import java.util.concurrent.*;

/**
 * @Author eddieVim
 * @微信公众号 艾迪威姆 / PositiveEddie
 * @Blog https://blog.csdn.net/weixin_44129784
 * @Create 2021/1/25 14:06
 * @Discription
 */
public class DisruptorSolution {

    private static class Pack {
        // 模拟大对象
        private long[] buffer = new long[100000];
        private String name;
        private int no;

        public int getNo() {
            return no;
        }

        public void setNo(int no) {
            this.no = no;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    // 用于生产者的多线程生产
    private ExecutorService executor;

    private int[] flag = new int[100];

    // 队列
    private Disruptor<Pack> disruptor;

    public DisruptorSolution() {
        this.executor = new ThreadPoolExecutor(5, 20,
                3L, TimeUnit.MINUTES,
                new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "Disruptor-Producer");
            }
        });

        // 生产者的线程工厂
        ThreadFactory threadFactory = new ThreadFactory(){
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "Disruptor-Consumer");
            }
        };

        // 用于创建事件
        EventFactory<Pack> eventFactory = new EventFactory<Pack>() {
            @Override
            public Pack newInstance() {
                return new Pack();
            }
        };



        // 阻塞策略
        BlockingWaitStrategy strategy = new BlockingWaitStrategy();

        // 指定RingBuffer的大小
        int bufferSize = 16;

        // 创建disruptor,采用多生产者模式
        disruptor = new Disruptor<>(eventFactory, bufferSize, threadFactory, ProducerType.MULTI, strategy);

        WorkHandler<Pack> workerHandler = new WorkHandler<Pack>() {
            @Override
            public void onEvent(Pack event) throws Exception {
                System.out.println("\nget a pack:" + event.getNo() + "\n开始模拟入库:");
                flag[event.getNo()]++;
                System.out.println("入库OK啦");
            }
        };
        disruptor.handleEventsWithWorkerPool(workerHandler, workerHandler, workerHandler);

        // 启动disruptor的线程
        disruptor.start();
    }

    /**
     * 引擎
     * @param pack
     */
    public void callBack(Pack pack) {
        // 获取disruptor中的缓冲区
        RingBuffer<Pack> ringBuffer = disruptor.getRingBuffer();
        // 获取该缓冲区中的下标
        long sequence = ringBuffer.next();

        try {
            Pack obj = ringBuffer.get(sequence);
            obj.setName(pack.getName());
            obj.setNo(pack.getNo());
        } finally {
            // 发布该事件
            ringBuffer.publish(sequence);
        }
    }

    /**
     * 模拟远程的引擎回调动作
     * 调用callBack回调结果
     *
     * @throws InterruptedException
     */
    public void simulateRemoteEngine() throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            Pack pack = new Pack();
            pack.setName("Name-" + i + "-name");
            pack.setNo(i);

            executor.submit(()->{
                callBack(pack);
            });
        }
    }

    public static void main(String[] args) throws InterruptedException {
        DisruptorSolution disruptorSolution = new DisruptorSolution();
        // 模拟远程发送
        disruptorSolution.simulateRemoteEngine();

        // 等待
        Thread.sleep(5000);

        if (disruptorSolution.check()) {
            System.out.println("\n\nNo problem! 没有并发问题!");
        }

        disruptorSolution.executor.shutdown();
        disruptorSolution.disruptor.shutdown();
    }

    private boolean check() {
        int count = 0;
        for (int i = 0; i < 100; i++) {
            if (this.flag[i] >= 2) {
                System.out.println("有并发问题!!index:" + i);
            } else {
                count++;
            }
        }
        return count == 100;
    }
}

微信公众号

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值