图解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 ...
随机推荐
- [Java]HashSet的工作原理
概述 This class implements the Set interface, backed by a hash table (actually a HashMap instance). It ...
- Luogu P1462 通往奥格瑞玛的道路 二分答案+最短路
先二分答案,再跑最短路,跑的时候遇到 过路费超过二分的答案的 就不拿他更新最短路 #include<cstdio> #include<iostream> #include< ...
- Java EE学习笔记(八)
动态SQL 1.动态SQL中的元素 1).作用:无需手动拼装SQL,MyBatis已提供的对SQL语句动态组装的功能,使得数据库开发效率大大提高! 2).动态SQL是MyBatis的强大特性之一,My ...
- spring boot 使用hibernate validator 验证service
不在controller中验证,而是在service中验证. spring boot 默认使用的就是hibernate validator,存在于pom的spring-boot-starter-web ...
- Java 数字数组随机数工具类 NumberUtils、ArrayUtils、RandomUtils用法
commons-lang3-3-3.8.1 //----------------------------------------------------------------------- /** ...
- P4874 回形遍历 —模拟
思路: 写完后信心满满,结果超时. 我很不解,下了个数据结果——,z竟然是大于1e10的,跟题目给的不一样啊 原来如此,正解是一行一行的走的... 注意当到两边一样近时,应优先向下和右!!!!!! 这 ...
- zeplin 登录效果实现
zeplin 登录效果实现 zeplin 登录页有个效果不错,https://app.zeplin.io/login 可以看看. 主要是输入框的字会随着状态变化而变化. 我这里实现了一个自己的效果 实 ...
- marquee标签(跑马灯)
- Java中的switch语句——通过示例学习Java编程(8)
作者:CHAITANYA SINGH 来源:https://www.koofun.com//pro/kfpostsdetail?kfpostsid=19 当我们在代码逻辑中有多个选项,而且需要为每个选 ...
- java编程基础二进制
0.java编程基础 01.二进制(原码,反码,补码) 02.位运算 03.移位运算符 二进制 原码,反码,补码 1.基本概念 二进制是逢2进位的进位制,0,1是基本算符. 现在的电子计算机技术全部使 ...