disruptor笔记之二:Disruptor类分析
欢迎访问我的GitHub
https://github.com/zq2599/blog_demos
内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;
《disruptor笔记》系列链接
本篇概览
- 通过前文的实战,咱们对Disruptor有了初步认识,借助com.lmax.disruptor.dsl.Disruptor类可以轻松完成以下操作:
- 环形队列初始化
- 指定事件消费者
- 启动消费者线程
- 接下来要面对两个问题:
- 深入了解Disruptor类是如何完成上述操作的;
- 对Disruptor类有了足够了解时,尝试不用Disruptor,自己动手操作环形队列,实现消息的生产和消费,这样做的目的是加深对Disruptor内部的认识,做到知其所以然;
- 接下来咱们先解决第一个问题吧,结合Disruptor对象的源码来看看上述三个操作到底做了什么;
环形队列初始化
- 环形队列初始化发生在实例化Disruptor对象的时候,即Disruptor的构造方法:
public Disruptor(final EventFactory<T> eventFactory, final int ringBufferSize, final ThreadFactory threadFactory)
{
this(RingBuffer.createMultiProducer(eventFactory, ringBufferSize), new BasicExecutor(threadFactory));
}
- RingBuffer.createMultiProducer方法内部实例化了RingBuffer,如下图红框:

- 记下第一个重要知识点:创建RingBuffer对象;
指定事件消费者
- 在前文中,下面这行代码指定了事件由StringEventHandler消费:
disruptor.handleEventsWith(new StringEventHandler(eventCountPrinter));
- 查看handleEventsWith方法的内部:
public final EventHandlerGroup<T> handleEventsWith(final EventHandler<? super T>... handlers)
{
return createEventProcessors(new Sequence[0], handlers);
}
- 展开createEventProcessors方法,如下图,请重点关注创建SequenceBarrier和BatchEventProcessor等操作:

- 展开上图红框四中的updateGatingSequencesForNextInChain方法,如下图,红框中的ringBuffer.addGatingSequences需要重点关注:

- 小结一下,disruptor.handleEventsWith方法涉及到四个重要知识点:
- 创建SequenceBarrier对象,用于接收ringBuffer中的可消费事件
- 创建BatchEventProcessor,负责消费事件
- 绑定BatchEventProcessor对象的异常处理类
- 调用ringBuffer.addGatingSequences,将消费者的Sequence传给ringBuffer
启动消费者线程
- 前文已通过日志确定了消费事件的逻辑是在一个独立的线程中执行的,启动消费者线程的代码如下:
disruptor.start();
- 展开start方法,如下可见,关键代码是consumerInfo.start(executor):
public RingBuffer<T> start()
{
checkOnlyStartedOnce();
for (final ConsumerInfo consumerInfo : consumerRepository)
{
consumerInfo.start(executor);
}
return ringBuffer;
}
- ConsumerInfo是接口,对应的实现类有EventProcessorInfo和WorkerPoolInfo两种,这里应该是哪种呢?既然来源是consumerRepository,这就要看当初是怎么存入consumerRepository的,前面在分析createEventProcessors方法时,下图红框中的consumerRepository.add被忽略了,现在需要进去看看:

- 进去后一目了然,可见ConsumerInfo的实现是EventProcessorInfo:

- 所以,回到前面对consumerInfo.start(executor)方法的分析,这里要看的就是EventProcessorInfo的start方法了,如下图,非常简单,就是启动一个线程执行eventprocessor(这个eventprocessor是BatchEventProcessor对象):

- 小结一下,disruptor.start方法涉及到一个重要知识点:
- 启动独立线程,用来执行消费事件的业务逻辑;
消费事件的逻辑
- 为了理解消息处理逻辑,还要重点关注BatchEventProcessor.processEvents方法,如下图所示,其实也很简单,就是不停的从环形队列取出可用的事件,然后再更新自己的Sequence,相当于标记已经消费到哪里了:

总结
最后总结Disruptor类的重要功能:
- 创建环形队列(RingBuffer对象)
- 创建SequenceBarrier对象,用于接收ringBuffer中的可消费事件
- 创建BatchEventProcessor,负责消费事件
- 绑定BatchEventProcessor对象的异常处理类
- 调用ringBuffer.addGatingSequences,将消费者的Sequence传给ringBuffer
- 启动独立线程,用来执行消费事件的业务逻辑
- 聪明的您一定会发现,本文并没有全面分析Disruptor类的源码,例如after、shutdown等方法都没有提到,确实如此,欣宸在此给您道歉了,本篇的重点是找出那些与基本功能有关代码,为后面的实战提供理论指导(不用Disruptor类实现消息生产消费的实战),因此很多高级功能都跳过了;
理解官方流程图
- 此时再看官方流程图,聪明的您应该很快就能理解此图表达的意思:每个消费者都有自己的Sequence,通过此Sequence取得自己在环形队列中消费的位置,再通过SequenceBarrier来等待可用事件的出现,等到事件出现了就用get方法取出具体的事件,给EventHandler来处理:

后续预告
- 此时,咱们对Disruptor类已经有了比较深入的理解,接下来的文章,咱们会尝试不用Disruptor类,仅凭着对RingBuffer对象的操作来实现以下三种功能:
- 100个事件,单个消费者消费;
- 100个事件,三个消费者,每个都独自消费这个100个事件;
- 100个事件,三个消费者共同消费这个100个事件;
你不孤单,欣宸原创一路相伴
欢迎关注公众号:程序员欣宸
微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...
https://github.com/zq2599/blog_demos
disruptor笔记之二:Disruptor类分析的更多相关文章
- Java笔记(二)类
类 一.类的基础 1.类---一种自定义数据类型. 2.与方法内创建局部变量不同,在创建对象的时候,所有的实例变量都会分配 一个默认值,这与创建数组的时候是类似的. 3.在{}对实例变量内赋值: in ...
- Wireshark学习笔记(二)取证分析案例详解
@ 目录 练习一:分析用户FTP操作 练习二:邮件读取 练习三:有人在摸鱼? 练习一:分析用户FTP操作 已知抓包文件中包含了用户登录FTP服务器并进行交互的一个过程,你能否通过wireshark分析 ...
- 《JAVA编程那点事儿》读书笔记(二)——类和对象
方法: 1. 基本的main方法: public static void main(String[] args) 2.静态方法内部调用非静态方法:重新声明一个类,通过这个类来调用非静态方法 publi ...
- objective-C学习笔记(二)类 class 和 结构 struct
Objective-C的类型 引用类型 类 class 指针 pointer 块 block 值类型 基础数值类型 结构 struct 枚举 enum 类型装饰 协议 protocol 类别 cate ...
- python cookbook第三版学习笔记十二:类和对象(三)创建新的类或实例属性
先介绍几个类中的应用__getattr__,__setattr__,__get__,__set__,__getattribute__,. __getattr__:当在类中找不到attribute的时候 ...
- Java学习笔记(二)——类和对象
[1]类是模子,确定对象将会拥有的特征(属性)和行为(方法). [2]类的特点:类是对象的类型: 具有相同属性和方法的一组对象的集合. [3]属性:对象具有的各种特征(每个对象的每个属性都拥有特定值) ...
- jvm的学习笔记:二、类的初始化,代码实战(3)
首次主动此用导致类的初始化 MyParent4 myParent4 = new MyParent4(); MyParent4 myParent5 = new MyParent4(); 输出: MyPa ...
- jvm的学习笔记:二、类的初始化,代码实战(2)
常量在编译阶段,会存在调用这个常量的方法的所在的类的常量池当中 System.out.println(MyParent2.str); 输出: hello parent2 依据:在MyTest2类调用M ...
- jvm的学习笔记:二、类的初始化,代码实战(1)
对于静态字段来说,直接定义该字段的类才会被初始化 System.out.println(MyChild1.str); 输出: myParent1 static block hello myParent ...
随机推荐
- ETL需求要求
前言 ETL落地dw层,dw层各表一般是由多个表关联取数得到的大宽表,在ETL需求中的dw设计应该考虑以下内容,目的是确保需求更清晰,开发和测试才能更高效的进行. 业务需求为基础 基于业务需求做足够多 ...
- Sqli-Labs less8-10
less-8 前置基础知识: 前几关我们用到了布尔盲注的办法,还有一种盲注就是时间盲注,不仅可以用于有回显的盲注,还能用于没有回显的盲注 函数:sleep(1):等待1秒之后再返回页面做出反应 IF( ...
- SQL 练习35
查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩 方式1: SELECT Student.sid,Student.sname,t.score from Student , (SELEC ...
- 攻防世界PWN简单题 level0
攻防世界PWN简单题 level0 开始考验栈溢出的相关知识了 Checksec 一下文件 看看都开了什么保护 和 是多少位的程序 发现是64位的程序, 扔进IDA64.IDA YYDS.. 进入主函 ...
- AECC2018同时中英文切换多开使用,加倍提高你的工作效率
最近相信不少人已经更新了AECC2018,升级之后第一件重要的事当然是中英文的切换了,要不然工作中很麻烦.对于一直习惯用中文的人来说,在用模板过程中会出现各种表达式报错极其不方便,而对于习惯英文操作朋 ...
- mysql行转列 问题 SUM(IF(条件,列值,0))
sum(if(条件,列值,0))语法用例: select name,sum(if(subject="语文",score,0)) as "语文" from gra ...
- 【java虚拟机】内存溢出解决思路
转自:https://blog.csdn.net/u013521220/article/details/79523633 内存溢出与数据库锁表的问题,可以说是开发人员的噩梦,一般的程序异常,总是可以知 ...
- 十三:Servlet3.0的异步
servlet之前的操作同时同步的,就是按照这样的一个流程来走的: 1.请求根据一个路径路由到一个servlet中, 2.servlet获取一系列的参数 3.执行一系列的逻辑(花费时间所占的比重也更大 ...
- php Abstract 抽象类 与 Interface的
一.Abstract Class 与 Interface 的构造 抽象类 Abstract Class <?php abstract class A { abstract public func ...
- 使用Operator State方式
使用 operator state的方式有以下几种: 方式一: stateful function(RichFunction) 实现 CheckpointFunction 接口 必须实现两个方法:Vo ...