图解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. python进阶12 Redis

    python进阶12 Redis 一.概念 #redis是一种nosql(not only sql)数据库,他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,还提 ...

  2. GYM 101933A(dp)

    要点 \(\sum{w_i} <= 1e8\)是有意味的. 设\(dp[i]\)为至少可以承受重量\(i\)的最大可达高度.转移时可以转移的\(j\)必须满足加上它之后得保证各层不能超重,所以\ ...

  3. Git关于Tag操作

    Git关于tag的操作 记录下git关于 tag的操作 列出所有标签 git tag : 列出所有的 git tag -l 'v1.2.4.*' : 最后一位任意匹配 新建标签 git tag -a ...

  4. 【手撸一个ORM】第九步、orm默认配置类 MyDbConfiguration,一次配置,简化实例化流程

    这个实现比较简单,事实上可配置的项目很多,如有需要,请读者自行扩展 using System; namespace MyOrm { public class MyDbConfiguration { p ...

  5. 练习三十二:用python实现:按相反的顺序输出列表的每一位值

    用python实现:按相反的顺序输出列表的每一位值 1. 使用list[::-1] list1 = ["one","two","three" ...

  6. 路径方案数(mod)

    路径方案数(mod) [题目描述] 给一张无向图,n 个点和 m 条边,cyb 在 1 号点,他要去 2 号点, cyb 可以从 a 走到 b,当且仅当a到2的最短路,比b 到2的最短路长. 求 cy ...

  7. h5复制粘贴板,打开APP功能

    <div class="container"> <img src="../themes/mall/img/i_red_ad4.jpg"> ...

  8. 复习KMP

    KMP刚学的时候,看不懂. 再看,哇!原来是这样! 用的时候,忘了. 为了不再跌倒,我决定,记住吧... 在我看来,KMP一般用于字符串匹配时的防超时优化. 他的精髓就是,利用已经匹配的信息,简化这之 ...

  9. Java 2 个 List 集合数据求并、补集操作

    开发过程中,我们可能需要对 2 个 或多个 List 集合中的数据进行处理,比如多个 List 集合数据求 相同元素,多个 List 集合数据得到只属于本身的数据,如图示: 这里写图片描述 这里以 2 ...

  10. HTML5:使用postMessage实现Ajax跨域请求

    HTML5:使用postMessage实现Ajax跨域请求 由于同源策略的限制,Javascript存在跨域通信的问题,典型的跨域问题有iframe与父级的通信等. 常规的几种解决方法: (1) do ...