disruptor 史上最全 之3: 8大使用场景 图解

文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:尼恩Java面试宝典 最新版 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取


disruptor 史上最全 系列文章:

作为Java领域最高性能的 队列,没有之一, 大家不光要懂,而是 需要深入骨髓的搞懂
尼恩用几篇文章,来进行一下详细介绍:

本文的最新的内容,请参见 电子书《Disruptor 红宝书:大厂必备,高端必备

领取《Disruptor 红宝书:大厂必备,高端必备》,加尼恩微信 ,请移步: 语雀 或者 码云

本文对 8大使用场景,做具体的分析

总的Disruptor 的使用场景

Disruptor 它可以用来作为高性能的有界内存队列, 适用于两大场景:

  • 生产者消费者场景
  • 发布订阅 场景

生产者消费者场景。Disruptor的最常用的场景就是“生产者-消费者”场景,对场景的就是“一个生产者、多个消费者”的场景,并且要求顺序处理。

备注,这里和JCTool 的 MPSC 队列,刚好相反, MPSC 使用于多生产者,单消费者场景

发布订阅 场景:Disruptor也可以认为是观察者模式的一种实现, 实现发布订阅模式。

当前业界开源组件使用Disruptor的包括Log4j2、Apache Storm等,

Disruptor 使用细分场景

Disruptor是一个优秀的并发框架,可以使用在多个生产者单消费者场景

  • 单生产者多消费者场景
  • 多生产者单消费者场景
  • 单生产者多消费者场景
  • 多个消费者串行消费场景
  • 菱形方式执行场景
  • 链式并行执行场景
  • 多组消费者相互隔离场景
  • 多组消费者航道执行模式

单生产者多消费者并行场景

在并发系统中提高性能最好的方式之一就是单一写者原则,对Disruptor也是适用的。

如果在生产者单消费者 需求中仅仅有一个事件生产者,那么可以设置为单一生产者模式来提高系统的性能。

在这里插入图片描述

ProducerType 的类型

ProducerType 定义了生产者的类型, 两类

在这里插入图片描述

在这种场景下,ProducerType 的类型的 SINGLE

说明:本文会以pdf格式持续更新,更多最新尼恩3高pdf笔记,请从下面的链接获取:语雀 或者 码云

单生产者多消费者并行场景的参考代码

参考的代码如下:

在这里插入图片描述

执行结果:

在这里插入图片描述

以上用例的具体减少,请参见 尼恩《100wqps 日志平台实操,视频》

多生产者单消费者场景

该场景较为简单,就是多个生产者,单个消费者

在这里插入图片描述

其实,消费者也可以是多个

ProducerType 的类型

ProducerType 定义了生产者的类型, 两类

在这里插入图片描述

在这种场景下,ProducerType 的类型的 MULTI

多生产者场景的要点

在代码编写维度,多生产者单消费者场景的要点如下:

  • 创建Disruptor 的时候,将ProducerType.SINGLE改为ProducerType.MULTI,

  • 编写多线程生产者的相关代码即可。

多生产者场景的参考代码

参考的代码如下:

在这里插入图片描述

运行的结果如下

在这里插入图片描述

以上用例的具体减少,请参见 尼恩《100wqps 日志平台实操,视频》

单生产者多消费者竞争场景

该场景中,生产者为一个,消费者为多个,多个消费者之间, 存在着竞争关系,

也就是说,对于同一个事件event ,多个消费者 不重复消费

在这里插入图片描述

disruptor如何设置多个竞争消费者?

首先,得了解一下,disruptor框架的两个设置消费者的方法

大概有两点:

  • 消费者需要 实现 WorkHandler 接口,而不是 EventHandler 接口
  • 使用 handleEventsWithWorkerPool 设置 disruptor的 消费者,而不是 handleEventsWith 方法

在disruptor框架调用start方法之前,有两个方法设置消费者:

  • disruptor.handleEventsWith(EventHandler … handlers),将多个EventHandler的实现类传入方法,封装成一个EventHandlerGroup,实现多消费者消费。
  • disruptor.handleEventsWithWorkerPool(WorkHandler … handlers),将多个WorkHandler的实现类传入方法,封装成一个EventHandlerGroup实现多消费者消费。

那么,以上的Disruptor类的handleEventsWith,handleEventsWithWorkerPool方法的联系及区别是什么呢?
相同的在于:

两者共同点都是,将多个消费者封装到一起,供框架消费事件。

第一个不同点在于:

对于某一条事件 event,

handleEventsWith 方法返回的EventHandlerGroup,Group中的每个消费者都会对 event 进行消费,各个消费者之间不存在竞争。

handleEventsWithWorkerPool方法返回的EventHandlerGroup,Group的消费者对于同一条事件 event 不重复消费;也就是,如果c0消费了事件m,则c1不再消费事件m。

另外一个不同:

在设置消费者的时候,Disruptor类的handleEventsWith,handleEventsWithWorkerPool方法所传入的形参不同。对于独立消费的消费者,应当实现EventHandler接口。对于不重复消费的消费者,应当实现WorkHandler接口。

因此,根据消费者集合是否独立消费事件,可以对不同的接口进行实现。也可以对两种接口同时实现,具体消费流程由disruptor的方法调用决定。

演示代码如下:

在这里插入图片描述

执行结果

在这里插入图片描述

以上用例的具体减少,请参见 尼恩《100wqps 日志平台实操,视频》

多个消费者串行消费场景

在 多个消费者串行消费场景中,多个消费者,可以按照次序,消费消息。

比如:一个用户注册的Event,需要有一个Handler来存储信息,一个Hanlder来发邮件等等。

在这里插入图片描述

多个消费者串行消费场景案例

在这里插入图片描述

执行结果

在这里插入图片描述

菱形方式执行场景

场景特点

先并发,后串行

在这里插入图片描述

菱形方式执行场景案例

在这里插入图片描述

执行结果

在这里插入图片描述

链式并行执行场景

场景特点

多组消费者形成 并行链,特点是:

  • 链内 串行

  • 链间 并行

img

场景案例

在这里插入图片描述

执行结果

在这里插入图片描述

多组消费者相互隔离场景

场景特点

多组消费者 相互隔离,特点是:

  • 组内 相互竞争

  • 组间 相互隔离

这里写图片描述

场景案例

在这里插入图片描述

执行结果

在这里插入图片描述

多组消费者航道执行模式

场景特点

多组消费者形成 并行链,特点是:

  • 组内 相互竞争

  • 组之间串行依次执行

这里写图片描述

场景案例

组之间串行依次执行,组内有多个实例竞争执行

在这里插入图片描述

执行效果

在这里插入图片描述

说明:本文会以pdf格式持续更新,更多最新尼恩3高pdf笔记,请从下面的链接获取:语雀 或者 码云

六边形执行顺序

这是一种比较复杂的场景

场景特点

单边内部是有序的

边和边之间是并行的

在这里插入图片描述

参考代码

   @org.junit.Test
    public void testHexagonConsumerDisruptorWithMethodRef() throws InterruptedException {
        // 消费者线程池
        Executor executor = Executors.newCachedThreadPool();
        // 环形队列大小,2的指数
        int bufferSize = 1024;
        // 构造  分裂者 (事件分发者)
        Disruptor<LongEvent> disruptor = new Disruptor<LongEvent>(LongEvent::new, bufferSize,
                executor,
                ProducerType.SINGLE,  //多个生产者
                new YieldingWaitStrategy());

        EventHandler consumer1 = new LongEventHandlerWithName("consumer 1");
        EventHandler consumer2 = new LongEventHandlerWithName("consumer 2");
        EventHandler consumer3 = new LongEventHandlerWithName("consumer 3");
        EventHandler consumer4 = new LongEventHandlerWithName("consumer 4");
        EventHandler consumer5 = new LongEventHandlerWithName("consumer 5");
        // 连接 消费者 处理器
        // 可以使用lambda来注册一个EventHandler

        disruptor.handleEventsWith(consumer1,consumer2);
        disruptor.after(consumer1).handleEventsWith(consumer3);
        disruptor.after(consumer2).handleEventsWith(consumer4);
        disruptor.after(consumer3,consumer4).handleEventsWith(consumer5);
        // 开启 分裂者(事件分发)
        disruptor.start();
        // 获取环形队列,用于生产 事件
        RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
        //1生产者,并发生产数据
        LongEventProducerWithTranslator producer = new LongEventProducerWithTranslator(ringBuffer);
        Thread thread = new Thread() {
            @Override
            public void run() {
                for (long i = 0; true; i++) {
                    producer.onData(i);
                    ThreadUtil.sleepSeconds(1);
                }
            }
        };
        thread.start();
        ThreadUtil.sleepSeconds(5);
    }

执行结果

在这里插入图片描述

参考文献

https://blog.csdn.net/hxg117/article/details/78064632
http://openjdk.java.net/projects/jdk8/features
http://beautynbits.blogspot.co.uk/2012/11/the-end-for-false-sharing-in-java.html
http://openjdk.java.net/jeps/142
http://mechanical-sympathy.blogspot.co.uk/2011/08/false-sharing-java-7.html
http://stackoverflow.com/questions/19892322/when-will-jvm-use-intrinsics
https://blogs.oracle.com/dave/entry/java_contented_annotation_to_help
https://www.jianshu.com/p/b38ffa33d64d
https://blog.csdn.net/MrYushiwen/article/details/123171635
https://blog.csdn.net/everyok/article/details/88889057

posted @ 2022-10-03 11:41  疯狂创客圈  阅读(5132)  评论(0编辑  收藏  举报