图解Disruptor框架(二):核心概念
图解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
- @Data
- public class Order {
- /**
- * 订单ID
- */
- private String id;
- /**
- * 订单名字
- */
- private String name;
- /**
- * 用于记录这个对象创建的时间
- */
- private Date createTime;
- }
订单工厂类OrderFactory。
- public class OrderFactory implements EventFactory<Order> {
- public Order newInstance() {
- Order order = new Order();
- order.setCreateTime(new Date());
- return order;
- }
- }
这个类主要用于ringbuffer构造的时候对其中存放的数据进行预加载。
相应的源码如下面所示:
- /**
- * 用于构造的时候需加载数据
- * @param eventFactory
- */
- private void fill(EventFactory<E> eventFactory)
- {
- for (int i = 0; i < bufferSize; i++)
- {
- // entries数组就是ringbuffer中用于存放数据的数组
- entries[BUFFER_PAD + i] = eventFactory.newInstance();
- }
- }
生产者Producer
- public class Producer {
- private RingBuffer<Order> ringBuffer;
- public Producer(RingBuffer<Order> ringBuffer) {
- this.ringBuffer = ringBuffer;
- }
- public void sendData(String s) {
- long sequence = ringBuffer.next();
- Order order = ringBuffer.get(sequence);
- order.setId(s);
- ringBuffer.publish(sequence);
- }
- }
消费者Comsumer
- @Data
- public class Comsumer implements WorkHandler<Order> {
- /**
- * 消费者ID
- */
- private String comsumerId;
- /**
- * 记录消费的次数
- */
- public static final AtomicInteger count = new AtomicInteger(0);
- public Comsumer(String comsumerId) {
- this.comsumerId = comsumerId;
- }
- @Override
- public void onEvent(Order event) throws Exception {
- System.out.println("消费者:"+comsumerId+"消费了数据,ID="+event.getId()+"Name="+event.getName());
- count.incrementAndGet();
- }
- }
主函数Main
- public class Main {
- public static void main(String[] args) throws InterruptedException {
- // 构造ringbuffer
- RingBuffer<Order> ringBuffer = RingBuffer.create(
- ProducerType.MULTI,
- new OrderFactory(),
- 1024*1024,
- new BlockingWaitStrategy()
- );
- // 创建一个序号屏障
- SequenceBarrier barrier = ringBuffer.newBarrier();
- // 创建消费者数组
- Comsumer[] comsumers = new Comsumer[10];
- for (int i = 0; i < comsumers.length; i++) {
- comsumers[i] = new Comsumer("comsumer"+i);
- }
- // 创建多消费者的工作池
- WorkerPool<Order> workerPool = new WorkerPool<Order>(
- ringBuffer,
- barrier,
- new EventExceptionHandler(),
- comsumers
- );
- // 设置多个消费者的sequence序号 用于单独统计消费进度, 并且设置到ringbuffer中
- ringBuffer.addGatingSequences(workerPool.getWorkerSequences());
- workerPool.start(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
- // -----消费者创建完毕-----
- // -----创建生产者------
- // 用于阻塞生产者生产数据
- CountDownLatch latch = new CountDownLatch(1);
- for(int i = 0; i < 100; i++) {
- final Producer producer = new Producer(ringBuffer);
- new Thread(new Runnable() {
- public void run() {
- try {
- // 阻塞生产者
- latch.await();
- } catch (Exception e) {
- e.printStackTrace();
- }
- for(int j = 0; j<100; j++) {
- producer.sendData(UUID.randomUUID().toString());
- }
- }
- }).start();
- }
- System.err.println("----------线程创建完毕,开始生产数据----------");
- // 放行生产者,让其生产数据
- latch.countDown();
- // 等待消费者消费完数据
- Thread.sleep(2000);
- System.err.println("消费次数:"+Comsumer.count);
- }
- static class EventExceptionHandler implements ExceptionHandler<Order> {
- public void handleEventException(Throwable ex, long sequence, Order event) {
- }
- public void handleOnStartException(Throwable ex) {
- }
- public void handleOnShutdownException(Throwable ex) {
- }
- }
- }
运行结果:

上述的代码主要就是生产者生产了10000个数据,然后消费者再消费这些数据。可以看到结果是正确的。
上述这个小小的demo就结束了。接下来会再看看Disruptor的操作,例如链式操作、菱形操作、多边形操作等等。
总结
这一个小节,阐述了Disruptor中的一些核心概念,并编写了一个helloworld程序。如果读者觉得还是比较难理解,那就多敲几遍。这个没有办法的。
项目源码地址: https://gitee.com/cjh95/disruptor_blog/tree/master
参考资料
图解Disruptor框架(二):核心概念的更多相关文章
- 图解Disruptor框架(一):初识Ringbuffer
图解Disruptor框架(一):初识Ringbuffer 概述 1. 什么是Disruptor?为什么是Disruptor? Disruptor是一个性能十分强悍的无锁高并发框架.在JUC并发包中, ...
- fusionjs 学习二 核心概念
核心概念 middleware 类似express 的中间件模型(实际上是构建在koa中间件模型上的),但是和koa 的中间件有差异 fusionjs 的中间件同时可以运行在浏览器页面加载的时候 se ...
- Spring框架的核心概念是什么?需要掌握的知识点都有哪些?
Spring其主要精髓 就是IOC和AOP.掌握好了这两点对于理解Spring的思想颇有意义. IOC(英文 Inversion of Control)就是控制反转的意思.就是把新建对象(new Ob ...
- 【ShardingSphere】ShardingSphere学习(二)-核心概念-SQL
逻辑表 水平拆分的数据库(表)的相同逻辑和数据结构表的总称. 例:订单数据根据主键尾数拆分为10张表,分别是t_order_0到t_order_9,他们的逻辑表名为t_order. 真实表 在分片的数 ...
- Disruptor并发框架 (二)核心概念场景分析
核心术语 RingBuffer(容器): 被看作Disruptor最主要的组件,然而从3.0开始RingBuffer仅仅负责存储和更新在Disruptor中流通的数据.对一些特殊的使用场景能够被用户( ...
- disruptor 核心概念 二
一.Disruptor图解 二.disruptor核心概念 1.RingBuffer到底是啥?正如名字所说的一样,他是一个环(首尾相接的环)它用做在不同上下文(线程)间传递数据的buffer Ring ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- Storm 学习之路(二)—— Storm核心概念详解
一.Storm核心概念 1.1 Topologies(拓扑) 一个完整的Storm流处理程序被称为Storm topology(拓扑).它是一个是由Spouts 和Bolts通过Stream连接起来的 ...
- Storm 系列(二)—— Storm 核心概念详解
一.Storm核心概念 1.1 Topologies(拓扑) 一个完整的 Storm 流处理程序被称为 Storm topology(拓扑).它是一个是由 Spouts 和 Bolts 通过 Stre ...
随机推荐
- python进阶12 Redis
python进阶12 Redis 一.概念 #redis是一种nosql(not only sql)数据库,他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,还提 ...
- GYM 101933A(dp)
要点 \(\sum{w_i} <= 1e8\)是有意味的. 设\(dp[i]\)为至少可以承受重量\(i\)的最大可达高度.转移时可以转移的\(j\)必须满足加上它之后得保证各层不能超重,所以\ ...
- Git关于Tag操作
Git关于tag的操作 记录下git关于 tag的操作 列出所有标签 git tag : 列出所有的 git tag -l 'v1.2.4.*' : 最后一位任意匹配 新建标签 git tag -a ...
- 【手撸一个ORM】第九步、orm默认配置类 MyDbConfiguration,一次配置,简化实例化流程
这个实现比较简单,事实上可配置的项目很多,如有需要,请读者自行扩展 using System; namespace MyOrm { public class MyDbConfiguration { p ...
- 练习三十二:用python实现:按相反的顺序输出列表的每一位值
用python实现:按相反的顺序输出列表的每一位值 1. 使用list[::-1] list1 = ["one","two","three" ...
- 路径方案数(mod)
路径方案数(mod) [题目描述] 给一张无向图,n 个点和 m 条边,cyb 在 1 号点,他要去 2 号点, cyb 可以从 a 走到 b,当且仅当a到2的最短路,比b 到2的最短路长. 求 cy ...
- h5复制粘贴板,打开APP功能
<div class="container"> <img src="../themes/mall/img/i_red_ad4.jpg"> ...
- 复习KMP
KMP刚学的时候,看不懂. 再看,哇!原来是这样! 用的时候,忘了. 为了不再跌倒,我决定,记住吧... 在我看来,KMP一般用于字符串匹配时的防超时优化. 他的精髓就是,利用已经匹配的信息,简化这之 ...
- Java 2 个 List 集合数据求并、补集操作
开发过程中,我们可能需要对 2 个 或多个 List 集合中的数据进行处理,比如多个 List 集合数据求 相同元素,多个 List 集合数据得到只属于本身的数据,如图示: 这里写图片描述 这里以 2 ...
- HTML5:使用postMessage实现Ajax跨域请求
HTML5:使用postMessage实现Ajax跨域请求 由于同源策略的限制,Javascript存在跨域通信的问题,典型的跨域问题有iframe与父级的通信等. 常规的几种解决方法: (1) do ...