图解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. [Java]HashSet的工作原理

    概述 This class implements the Set interface, backed by a hash table (actually a HashMap instance). It ...

  2. Luogu P1462 通往奥格瑞玛的道路 二分答案+最短路

    先二分答案,再跑最短路,跑的时候遇到 过路费超过二分的答案的 就不拿他更新最短路 #include<cstdio> #include<iostream> #include< ...

  3. Java EE学习笔记(八)

    动态SQL 1.动态SQL中的元素 1).作用:无需手动拼装SQL,MyBatis已提供的对SQL语句动态组装的功能,使得数据库开发效率大大提高! 2).动态SQL是MyBatis的强大特性之一,My ...

  4. spring boot 使用hibernate validator 验证service

    不在controller中验证,而是在service中验证. spring boot 默认使用的就是hibernate validator,存在于pom的spring-boot-starter-web ...

  5. Java 数字数组随机数工具类 NumberUtils、ArrayUtils、RandomUtils用法

    commons-lang3-3-3.8.1 //----------------------------------------------------------------------- /** ...

  6. P4874 回形遍历 —模拟

    思路: 写完后信心满满,结果超时. 我很不解,下了个数据结果——,z竟然是大于1e10的,跟题目给的不一样啊 原来如此,正解是一行一行的走的... 注意当到两边一样近时,应优先向下和右!!!!!! 这 ...

  7. zeplin 登录效果实现

    zeplin 登录效果实现 zeplin 登录页有个效果不错,https://app.zeplin.io/login 可以看看. 主要是输入框的字会随着状态变化而变化. 我这里实现了一个自己的效果 实 ...

  8. marquee标签(跑马灯)

  9. Java中的switch语句——通过示例学习Java编程(8)

    作者:CHAITANYA SINGH 来源:https://www.koofun.com//pro/kfpostsdetail?kfpostsid=19 当我们在代码逻辑中有多个选项,而且需要为每个选 ...

  10. java编程基础二进制

    0.java编程基础 01.二进制(原码,反码,补码) 02.位运算 03.移位运算符 二进制 原码,反码,补码 1.基本概念 二进制是逢2进位的进位制,0,1是基本算符. 现在的电子计算机技术全部使 ...