图解Disruptor框架(二):核心概念

概述

上一个章节简单的介绍了了下Disruptor,这节就是要好好的理清楚Disruptor中的核心的概念。并且会给出个HelloWorld的小例子。

在正式开始之前,我觉得有一点我感触非常的深刻,那就是:外国人取的类名真的真的非常的合适以及形象!看看接下来的内容就知道了!

核心概念介绍

下面这张图,非常好的总结了Disruptor中需要了解的核心概念:


多生产者多消费者模型
  • RingBuffer: Disruptor中的数据结构,用于存储生产者生产的数据(在Disruptor中,叫做Event)。

  • Sequence:序号。在Disruptor框架中,任何地方都有序号。生产者生产的数据放在RingBuffer中的哪个位置,消费者应该消费哪个位置的数据,RingBuffer中的某个位置的数据是什么,这些都是由这个序号来决定的。这个序号可以简单的理解为一个AtomicLong类型的变量。其使用了padding的方法去消除缓存的伪共享问题。

  • Sequencer:序号生成器。这个类主要是用来协调生产者的。在生产者生产数据的时候,Sequencer会产生一个可用的序号(Sequence),然后生产者就乖乖的把数据放在那里了。(此处不严谨,后续会说明原因。)

  • SequencerBarrier:序号屏障。(我觉得这个名字真的太形象了!)我们都知道,消费者在消费数据的时候,需要知道消费哪个位置的数据。消费者总不能自己想取哪个数据消费,就取哪个数据消费吧。(这样多混乱啊!)这个SequencerBarrier起到的就是这样一个“栅栏”般的阻隔作用。你消费者想消费数据,得,我告诉你一个序号(Sequence),你去消费那个位置上的数据。要是没有数据,就好好等着吧(怎么等也是有讲究的)。

先小结一下:Sequence、Sequencer、SequencerBarrier这三个概念一开始我是比较难以理解的。但是总结一下,无非就是Ringbuffer中哪里都需要用到序号,而Sequencer用于生产者生产的时候产生序号,SeqencerBarrier就是协调生产者与消费者,并且告诉消费者一个可以用于消费的序号!

  • Wait Strategy:等待策略。设想一种这样的情景:生产者生产的非常慢,而消费者消费的非常快。那么必然会出现数据不够的情况,这个时候消费者怎么进行等待呢?WaitStrategy就是为了解决问题而诞生的。

  • Event:数据、事件。这个Event就是我们希望RingBuffer存储的数据类型。这个是我们用户自己定义的。我们可以定义为任何事情,比如处理订单、消息等等。

  • EventHandler:事件处理器。当RingBuffer中有数据的时候,消费者怎么去对数据进行处理,就是由这个类去决定的。

  • Producer:生产者。用于生产数据。

上述就是Disruptor框架中的核心概念。如果不是非常的理解,可以先跟着我下面的例子去手敲一遍入门的程序,去体验一下。然后看看源码加深理解。

第一个入门小程序

首先是订单类Order


  1. @Data 

  2. public class Order { 


  3. /** 

  4. * 订单ID 

  5. */ 

  6. private String id; 


  7. /** 

  8. * 订单名字 

  9. */ 

  10. private String name; 


  11. /** 

  12. * 用于记录这个对象创建的时间 

  13. */ 

  14. private Date createTime; 




订单工厂类OrderFactory

  1. public class OrderFactory implements EventFactory<Order> { 

  2. public Order newInstance() { 

  3. Order order = new Order(); 

  4. order.setCreateTime(new Date()); 

  5. return order; 





这个类主要用于ringbuffer构造的时候对其中存放的数据进行预加载。

相应的源码如下面所示:


  1. /** 

  2. * 用于构造的时候需加载数据 

  3. * @param eventFactory 

  4. */ 

  5. private void fill(EventFactory<E> eventFactory) 



  6. for (int i = 0; i < bufferSize; i++) 



  7. // entries数组就是ringbuffer中用于存放数据的数组 

  8. entries[BUFFER_PAD + i] = eventFactory.newInstance(); 





生产者Producer


  1. public class Producer { 


  2. private RingBuffer<Order> ringBuffer; 



  3. public Producer(RingBuffer<Order> ringBuffer) { 

  4. this.ringBuffer = ringBuffer; 




  5. public void sendData(String s) { 

  6. long sequence = ringBuffer.next(); 

  7. Order order = ringBuffer.get(sequence); 

  8. order.setId(s); 

  9. ringBuffer.publish(sequence); 





消费者Comsumer


  1. @Data 

  2. public class Comsumer implements WorkHandler<Order> { 


  3. /** 

  4. * 消费者ID 

  5. */ 

  6. private String comsumerId; 


  7. /** 

  8. * 记录消费的次数 

  9. */ 

  10. public static final AtomicInteger count = new AtomicInteger(0); 



  11. public Comsumer(String comsumerId) { 

  12. this.comsumerId = comsumerId; 




  13. @Override 

  14. public void onEvent(Order event) throws Exception { 

  15. System.out.println("消费者:"+comsumerId+"消费了数据,ID="+event.getId()+"Name="+event.getName()); 

  16. count.incrementAndGet(); 





主函数Main


  1. public class Main { 

  2. public static void main(String[] args) throws InterruptedException { 


  3. // 构造ringbuffer 

  4. RingBuffer<Order> ringBuffer = RingBuffer.create( 

  5. ProducerType.MULTI, 

  6. new OrderFactory(), 

  7. 1024*1024, 

  8. new BlockingWaitStrategy() 

  9. ); 


  10. // 创建一个序号屏障 

  11. SequenceBarrier barrier = ringBuffer.newBarrier(); 


  12. // 创建消费者数组 

  13. Comsumer[] comsumers = new Comsumer[10]; 

  14. for (int i = 0; i < comsumers.length; i++) { 

  15. comsumers[i] = new Comsumer("comsumer"+i); 




  16. // 创建多消费者的工作池 

  17. WorkerPool<Order> workerPool = new WorkerPool<Order>( 

  18. ringBuffer, 

  19. barrier, 

  20. new EventExceptionHandler(), 

  21. comsumers 

  22. ); 


  23. // 设置多个消费者的sequence序号 用于单独统计消费进度, 并且设置到ringbuffer中 

  24. ringBuffer.addGatingSequences(workerPool.getWorkerSequences()); 


  25. workerPool.start(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())); 


  26. // -----消费者创建完毕----- 



  27. // -----创建生产者------ 

  28. // 用于阻塞生产者生产数据 

  29. CountDownLatch latch = new CountDownLatch(1); 


  30. for(int i = 0; i < 100; i++) { 

  31. final Producer producer = new Producer(ringBuffer); 

  32. new Thread(new Runnable() { 

  33. public void run() { 

  34. try { 

  35. // 阻塞生产者 

  36. latch.await(); 

  37. } catch (Exception e) { 

  38. e.printStackTrace(); 



  39. for(int j = 0; j<100; j++) { 

  40. producer.sendData(UUID.randomUUID().toString()); 





  41. }).start(); 




  42. System.err.println("----------线程创建完毕,开始生产数据----------"); 

  43. // 放行生产者,让其生产数据 

  44. latch.countDown(); 


  45. // 等待消费者消费完数据 

  46. Thread.sleep(2000); 


  47. System.err.println("消费次数:"+Comsumer.count); 





  48. static class EventExceptionHandler implements ExceptionHandler<Order> { 

  49. public void handleEventException(Throwable ex, long sequence, Order event) { 




  50. public void handleOnStartException(Throwable ex) { 




  51. public void handleOnShutdownException(Throwable ex) { 







运行结果:

上述的代码主要就是生产者生产了10000个数据,然后消费者再消费这些数据。可以看到结果是正确的。

上述这个小小的demo就结束了。接下来会再看看Disruptor的操作,例如链式操作、菱形操作、多边形操作等等。

总结

这一个小节,阐述了Disruptor中的一些核心概念,并编写了一个helloworld程序。如果读者觉得还是比较难理解,那就多敲几遍。这个没有办法的。

项目源码地址: https://gitee.com/cjh95/disruptor_blog/tree/master

参考资料

  1. 官网的简单介绍 https://github.com/LMAX-Exchange/disruptor/wiki/Introduction
  2. 伪共享 https://www.cnblogs.com/cyfonly/p/5800758.html

图解Disruptor框架(二):核心概念的更多相关文章

  1. 图解Disruptor框架(一):初识Ringbuffer

    图解Disruptor框架(一):初识Ringbuffer 概述 1. 什么是Disruptor?为什么是Disruptor? Disruptor是一个性能十分强悍的无锁高并发框架.在JUC并发包中, ...

  2. fusionjs 学习二 核心概念

    核心概念 middleware 类似express 的中间件模型(实际上是构建在koa中间件模型上的),但是和koa 的中间件有差异 fusionjs 的中间件同时可以运行在浏览器页面加载的时候 se ...

  3. Spring框架的核心概念是什么?需要掌握的知识点都有哪些?

    Spring其主要精髓 就是IOC和AOP.掌握好了这两点对于理解Spring的思想颇有意义. IOC(英文 Inversion of Control)就是控制反转的意思.就是把新建对象(new Ob ...

  4. 【ShardingSphere】ShardingSphere学习(二)-核心概念-SQL

    逻辑表 水平拆分的数据库(表)的相同逻辑和数据结构表的总称. 例:订单数据根据主键尾数拆分为10张表,分别是t_order_0到t_order_9,他们的逻辑表名为t_order. 真实表 在分片的数 ...

  5. Disruptor并发框架 (二)核心概念场景分析

    核心术语 RingBuffer(容器): 被看作Disruptor最主要的组件,然而从3.0开始RingBuffer仅仅负责存储和更新在Disruptor中流通的数据.对一些特殊的使用场景能够被用户( ...

  6. disruptor 核心概念 二

    一.Disruptor图解 二.disruptor核心概念 1.RingBuffer到底是啥?正如名字所说的一样,他是一个环(首尾相接的环)它用做在不同上下文(线程)间传递数据的buffer Ring ...

  7. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  8. Storm 学习之路(二)—— Storm核心概念详解

    一.Storm核心概念 1.1 Topologies(拓扑) 一个完整的Storm流处理程序被称为Storm topology(拓扑).它是一个是由Spouts 和Bolts通过Stream连接起来的 ...

  9. Storm 系列(二)—— Storm 核心概念详解

    一.Storm核心概念 1.1 Topologies(拓扑) 一个完整的 Storm 流处理程序被称为 Storm topology(拓扑).它是一个是由 Spouts 和 Bolts 通过 Stre ...

随机推荐

  1. SpringBoot---Web开发---WebSocket

    [广播式] 1. <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht ...

  2. [RDL]中多行组列组占比报表制作

    结果如下: 生意额占比表达式:=iif(Fields!生意额.Value is nothing,"",Fields!生意额.Value/sum(Fields!生意额.Value, ...

  3. wepy开发踩坑记录

    与vue的不同 methods对象只存放tap等事件触发时的方法 events对象只存放$emit及$broadcast方法触发的事件 自定义方法及属性放在与methods平级的位置 props是动态 ...

  4. 洛谷 P1969 积木大赛

    题目描述 春春幼儿园举办了一年一度的“积木大赛”.今年比赛的内容是搭建一座宽度为n的大厦,大厦可以看成由n块宽度为1的积木组成,第i块积木的最终高度需要是hi. 在搭建开始之前,没有任何积木(可以看成 ...

  5. 使用 swift3.0高仿新浪微博

    项目地址:https://github.com/SummerHH/swift3.0WeBo 使用 swift3.0 高仿微博,目前以实现的功能有,添加访客视图,用户信息授权,首页数据展示(支持正文中连 ...

  6. Spring的配置及jar包下载

    一.相关说明 IOC: Inversion of Control,控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度.其中最常见的方式叫做依赖注入(Dependency In ...

  7. 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here

    你的飞碟在这儿 难度:☆ Code: #include<iostream> #include<cstring> #include<cstdio> using nam ...

  8. 本号讯 | 永不消失的协作“空间站”开课;微软推出微软云Azure文档网站

    8月29日,针对企业常面临的“协同办公”困难,开展以“还有这种操作?永不消失的协作'空间站'”为主题的协同办公培训课. 课程内容包含:在Office 365环境中,如何利用Teams与Groups等功 ...

  9. SQL Server数据库log shipping 灾备(Part2 )

    3.配置步骤: 主服务器(A机)设置 (1) 启用Log Shipping Configuration 右键单击需要配置日志传输的数据库->Tasks-> Ship Transaction ...

  10. C#中Image类与byte[]之间的转换

    //将image转化为二进制 public byte[] GetByteImage(Image img) { byte[] bt = null; if (!img.Equals(null)) { us ...