图解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 ...
随机推荐
- Core2.0 项目到2.1
Core2.0 项目到2.1 https://www.cnblogs.com/FlyLolo/p/ASPNETCore2_10.html .NET Core 2.1 终于发布了, 赶紧升级一下. 一. ...
- oracle rollback 观察时间
###########issue 0: db alert 有如下提示, thread 1 cannot allocatete new log, sequenec 1111 通过检查v$log ,发现1 ...
- Linux Ubuntu系统之PPP拨号经验分享
近期,工作需要,我负责开发PPP拨号模块. 说起拨号,算算时间,我已经做过2次了, 暴露年龄了,呵呵. 第一次是刚毕业做的PPOE拨号,给电信做拨号软件,在河北石家庄工作过一段时间,基于windows ...
- webpack.config.js====插件html-webpack-plugin
1. 安装 cnpm install html-webpack-plugin --save-dev 2. webpack.config.js中使用 const htmlWebpackPlugin = ...
- MySQL++简单使用记录.md
#1.简介 MySQL++ is a powerful C++ wrapper for MySQL’s C API. Its purpose is to make working with queri ...
- Beginning Python Chapter 2 Notes
Python基本数据类型用Python官方说法应该叫Python内建数据类型,英文叫built-in type.下面稍微总结了一下我看到过的Python内建数据类型. Python基本数据类型 数据类 ...
- PostgreSQL缓存
目录[-] pg_buffercache pgfincore pg_prewarm dstat Linux ftools 使用pg_prewarm预加载关系/索引: pgfincore 输出: 怎样刷 ...
- hihoCoder #1165 : 益智游戏 (挑战赛11 B题)
题意:在一个序列中找到两个数a和b,使得a*b的因子个数最多,输出最多的因子个数. 思路:数据较多,处理会很慢.对序列中每个数字进行质数分解求因子个数,然后按照因子个数降序排列,对前50个因子最多的数 ...
- UIView Border color
// // UIView+Borders.h // // Created by Aaron Ng on 12/28/13. // Copyright (c) 2013 Delve. All right ...
- python3.6 配置COCO API出错解决方案
使用Anaconda Prompt进行安装 问题出现的背景:在尝试使用mask-rcnn时,遇到了这个问题,最终解决掉了