Disruptor的简单介绍与应用
前言
最近工作比较忙,在工作项目中,看了很多人都自己实现了一套数据任务处理机制,个人感觉有点乱,且也方便他人的后续维护,所以想到了一种数据处理模式,即生产者、缓冲队列、消费者的模式来统一大家的实现逻辑。
下面时是对Disruptor基本使用的演示。使用中需要引入依赖
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
名称解释
Ring Buffer
环境的缓存区,3.0版本以前被认为是Disruptor的主要成员。3.0版本以后,环形缓冲区只负责通过Disruptor的事件方式来对数据进行存储和更新。在一些高级的应用场景中,Ring Buffer可以由用户的自定义实现完全替代。
Sequence
Disruptor使用Sequence作为一种方法来确定特定组件的位置。每个使用者(EventProcessor)与Disruptor本身一样维护一个序列。大多数并发代码依赖于这些序列值的移动,因此序列支持AtomicLong的许多当前特性。事实上,两者之间唯一真正的区别是序列包含额外的功能,以防止序列和其他值之间的错误共享。
Sequencer
Sequencer是真正的核心,该接口的两个实现(单生产者, 多消费者)实现了所有用于在生产者和使用者之间的快速、正确的传递数据的并发算法。
Sequence Barrier
序列屏障由Sequencer产生,包含对Sequencer和任何依赖消费者的序列的引用。它包含确定是否有任何事件可供使用者处理的逻辑。
Wait Strategy
等待策略确定消费者将如何等待生产者产生的消息,Disruptor将消息放到事件(Event)中。
Event
从生产者到消费者的数据单位。不存在完全由用户定义的事件的特定代码的表示形式。
EventProcessor
EventProcessor持有特定消费者(Consumer)的Sequence,并提供用于调用事件处理实现的事件循环。
BatchEventProcessor
BatchEventProcessor它包含事件循环的有效实现,并将回调到已使用的EventHandle接口实现。
EventHandler
Disruptor定义的事件处理接口,由用户实现,用于处理事件,是Consumer的真正实现。
Producer
生产者,只是泛指调用Disruptor发布事件的用户代码,Disruptor没有定义特定接口或类型。
架构图
简单实用Disruptor
1 定义事件
事件就是通过Disruptor进行交换的数据类型。
package com.disruptor;
public class Data {
private long value;
public long getValue() {
return value;
}
public void setValue(long value) {
this.value = value;
}
}
2 定义事件工厂
事件工厂定义了如何实例化第一步中定义的事件。Disruptor通过EventFactory在RingBuffer中预创建Event的实例。
一个Event实例被用作一个数据槽,发布者发布前,先从RingBuffer获得一个Event的实例,然后往Event实例中插入数据,然后再发布到RingBuffer中,最后由Consumer获得Event实例并从中读取数据。
package com.disruptor;
import com.lmax.disruptor.EventFactory;
public class DataFactory implements EventFactory<Data> {
@Override
public Data newInstance() {
return new Data();
}
}
3 定义生产者
package com.disruptor;
import com.lmax.disruptor.RingBuffer;
import java.nio.ByteBuffer;
public class Producer {
private final RingBuffer<Data> ringBuffer;
public Producer(RingBuffer<Data> ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void pushData(ByteBuffer byteBuffer) {
long sequence = ringBuffer.next();
try {
Data even = ringBuffer.get(sequence);
even.setValue(byteBuffer.getLong(0));
} finally {
ringBuffer.publish(sequence);
}
}
}
4 定义消费者
package com.disruptor;
import com.lmax.disruptor.WorkHandler;
import java.text.MessageFormat;
public class Consumer implements WorkHandler<Data> {
@Override
public void onEvent(Data data) throws Exception {
long result = data.getValue() + 1;
System.out.println(MessageFormat.format("Data process : {0} + 1 = {1}", data.getValue(), result));
}
}
5 启动Disruptor
- 测试Demo
package com.disruptor;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import java.nio.ByteBuffer;
import java.util.concurrent.ThreadFactory;
public class Main {
private static final int NUMS = 10;
private static final int SUM = 1000000;
public static void main(String[] args) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long start = System.currentTimeMillis();
DataFactory factory = new DataFactory();
int buffersize = 1024;
Disruptor<Data> disruptor = new Disruptor<Data>(factory, buffersize, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
});
Consumer[] consumers = new Consumer[NUMS];
for (int i = 0; i < NUMS; i++) {
consumers[i] = new Consumer();
}
disruptor.handleEventsWithWorkerPool(consumers);
disruptor.start();
RingBuffer<Data> ringBuffer = disruptor.getRingBuffer();
Producer producer = new Producer(ringBuffer);
ByteBuffer bb = ByteBuffer.allocate(8);
for (long i = 0; i < SUM; i++) {
bb.putLong(0, i);
producer.pushData(bb);
System.out.println("Success producer data : " + i);
}
long end = System.currentTimeMillis();
disruptor.shutdown();
System.out.println("Total time : " + (end - start));
}
}
- 结果(部分结果展示)
Data process : 999,987 + 1 = 999,988
Success producer data : 999995
Data process : 999,990 + 1 = 999,991
Data process : 999,989 + 1 = 999,990
Data process : 999,991 + 1 = 999,992
Data process : 999,992 + 1 = 999,993
Data process : 999,993 + 1 = 999,994
Data process : 999,995 + 1 = 999,996
Success producer data : 999996
Success producer data : 999997
Success producer data : 999998
Success producer data : 999999
Data process : 999,994 + 1 = 999,995
Data process : 999,996 + 1 = 999,997
Data process : 999,997 + 1 = 999,998
Data process : 999,998 + 1 = 999,999
Data process : 999,999 + 1 = 1,000,000
Total time : 14202
由结果展示可见,边生产、边消费。
彩蛋
1 事件转换类
package com.mm.demo.disruptor.translator;
import com.lmax.disruptor.EventTranslatorOneArg;
import com.mm.demo.disruptor.entity.Data;
public class DataEventTranslator implements EventTranslatorOneArg<Data, Long> {
@Override
public void translateTo(Data event, long sequence, Long arg0) {
System.out.println(MessageFormat.format("DataEventTranslator arg0 = {0}, seq = {1}", arg0, sequence));
event.setValue(arg0);
}
}
2 消费者
2.1 消费者Demo1
消费者每次将event的结果加1。
package com.mm.demo.disruptor.handler;
import com.lmax.disruptor.EventHandler;
import com.mm.demo.disruptor.entity.Data;
import java.text.MessageFormat;
public class D1DataEventHandler implements EventHandler<Data> {
@Override
public void onEvent(Data event, long sequence, boolean endOfBatch) throws Exception {
long result = event.getValue() + 1;
Thread t = new Thread();
String name = t.getName();
System.out.println(MessageFormat.format("consumer "+name+": {0} + 1 = {1}", event.getValue(), result));
}
}
这里是使用的是EventHandler。也是使用WorkHandler,EventHandler和WorkHandler的区别是前者不需要池化,后者需要池化。
2.2 消费者Demo2
package com.mm.demo.disruptor.handler;
import com.lmax.disruptor.EventHandler;
import com.mm.demo.disruptor.entity.Data;
import java.text.MessageFormat;
public class D2DataEventHandler implements EventHandler<Data> {
@Override
public void onEvent(Data event, long sequence, boolean endOfBatch) throws Exception {
long result = event.getValue() + 2;
System.out.println(MessageFormat.format("consumer 2: {0} + 2 = {1}", event.getValue(), result));
}
}
2.3 串行依次计算
Consumer1执行完成再执行Consumer2。
package com.mm.demo.disruptor.process;
import com.lmax.disruptor.dsl.Disruptor;
import com.mm.demo.disruptor.entity.Data;
import com.mm.demo.disruptor.handler.D1DataEventHandler;
import com.mm.demo.disruptor.handler.D2DataEventHandler;
/**
* 串行依次计算
* @DateT: 2020-01-07
*/
public class Serial {
public static void serial(Disruptor<Data> disruptor) {
disruptor.handleEventsWith(new D1DataEventHandler()).then(new D2DataEventHandler());
disruptor.start();
}
}
2.4 并行实时计算
Consumer1和Consumer2同时执行。
package com.mm.demo.disruptor.process;
import com.lmax.disruptor.dsl.Disruptor;
import com.mm.demo.disruptor.entity.Data;
import com.mm.demo.disruptor.handler.D1DataEventHandler;
import com.mm.demo.disruptor.handler.D2DataEventHandler;
/**
* 并行执行
* @DateT: 2020-01-07
*/
public class Parallel {
public static void parallel(Disruptor<Data> dataDisruptor) {
dataDisruptor.handleEventsWith(new D1DataEventHandler(), new D2DataEventHandler());
dataDisruptor.start();
}
}
2.5 测试类
package com.mm.demo.disruptor;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.mm.demo.disruptor.entity.Data;
import com.mm.demo.disruptor.handler.D1DataEventHandler;
import com.mm.demo.disruptor.process.Parallel;
import com.mm.demo.disruptor.process.Serial;
import com.mm.demo.disruptor.translator.DataEventTranslator;
import javax.swing.plaf.synth.SynthTextAreaUI;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class Main {
private static final int BUFFER = 1024 * 1024;
public static void main(String[] args) {
DataFactory factory = new DataFactory();
Disruptor<Data> disruptor = new Disruptor<Data>(factory, BUFFER, Executors.defaultThreadFactory(), ProducerType.MULTI, new BlockingWaitStrategy());
Serial.serial(disruptor);
// Parallel.parallel(disruptor);
RingBuffer<Data> ringBuffer = disruptor.getRingBuffer();
for (int i = 0; i < 2; i++) {
ringBuffer.publishEvent(new DataEventTranslator(), (long)i);
}
disruptor.shutdown();
}
}
总结
上边只演示了串行和并行的方式,其实还是通过组合的方式创建不的计算处理方式(需要创建多个事件处理器EventHandler)。
补充等待策略
- BlockingWaitStrategy:最低效的策略,但是对cpu的消耗是最小的,在各种不同部署环境中能提供更加一致的性能表现。
- SleepingWaitStrategy:性能和BlockingWaitStrategy差不多少,cpu消耗也类似,但是其对生产者线程的影响最小,适合用于异步处理数据的场景。
- YieldingWaitStrategy:性能是最好的,适用于低延迟的场景。在要求极高性能且事件处理线程数小于cpu处理核数时推荐使用此策略。
- BusySpinWaitStrategy:低延迟,但是对cpu资源的占用较多。
- PhasedBackoffWaitStrategy:上边几种策略的综合体,延迟大,但是占用cpu资源较少。
来源:站长平台
Disruptor的简单介绍与应用的更多相关文章
- [原创]关于mybatis中一级缓存和二级缓存的简单介绍
关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...
- 利用Python进行数据分析(7) pandas基础: Series和DataFrame的简单介绍
一.pandas 是什么 pandas 是基于 NumPy 的一个 Python 数据分析包,主要目的是为了数据分析.它提供了大量高级的数据结构和对数据处理的方法. pandas 有两个主要的数据结构 ...
- 利用Python进行数据分析(4) NumPy基础: ndarray简单介绍
一.NumPy 是什么 NumPy 是 Python 科学计算的基础包,它专为进行严格的数字处理而产生.在之前的随笔里已有更加详细的介绍,这里不再赘述. 利用 Python 进行数据分析(一)简单介绍 ...
- yii2的权限管理系统RBAC简单介绍
这里有几个概念 权限: 指用户是否可以执行哪些操作,如:编辑.发布.查看回帖 角色 比如:VIP用户组, 高级会员组,中级会员组,初级会员组 VIP用户组:发帖.回帖.删帖.浏览权限 高级会员组:发帖 ...
- angular1.x的简单介绍(二)
首先还是要强调一下DI,DI(Denpendency Injection)伸手获得,主要解决模块间的耦合关系.那么模块是又什么组成的呢?在我看来,模块的最小单位是类,多个类的组合就是模块.关于在根模块 ...
- Linux的简单介绍和常用命令的介绍
Linux的简单介绍和常用命令的介绍 本说明以Ubuntu系统为例 Ubuntu系统的安装自行百度,或者参考http://www.cnblogs.com/CoderJYF/p/6091068.html ...
- iOS-iOS开发简单介绍
概览 终于到了真正接触IOS应用程序的时刻了,之前我们花了很多时间去讨论C语言.ObjC等知识,对于很多朋友而言开发IOS第一天就想直接看到成果,看到可以运行的IOS程序.但是这里我想强调一下,前面的 ...
- iOS开发多线程篇—多线程简单介绍
iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...
- iOS开发UI篇—UITabBarController简单介绍
iOS开发UI篇—UITabBarController简单介绍 一.简单介绍 UITabBarController和UINavigationController类似,UITabBarControlle ...
随机推荐
- 这篇干货让你在零点前完成学术Essay写作
写论文,做研究,上课,参加课外活动,与他人social...在美国,你会有很多的事情需要你去做,如何将自己的时间平衡的分配到自己的学习生活以及私人生活中,就显得尤为重要,而这些问题也是影响中国学生的重 ...
- Elasticsearch 删除文档
章节 Elasticsearch 基本概念 Elasticsearch 安装 Elasticsearch 使用集群 Elasticsearch 健康检查 Elasticsearch 列出索引 Elas ...
- POJ 1423:Big Number 求N的阶乘的长度 斯特林公式
Big Number Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 27027 Accepted: 8626 Descr ...
- PHP ~ 通过程序删除图片,同时删除数据库中的图片数据 和 图片文件
删除单张图片 <?php require_once '../../conn.php'; //连接数据库 $ID = $_GET['ID' ...
- Charles中windows版本解决response乱码问题
实际上三种,目前写了两种,加了之后有的不显示乱码,但是有的还是显示,第三种搜索结果是安装证书,但是本人安装后证书后未受到信任,所以暂时不知是否能够成功 1,在charles.ini中,手动增加一个vm ...
- python运算表达式
运算符1.算术运算符:+,-,*,/,//(求整商),%,**(求多次方,左边为数,右边为多少次方)2.关系运算符:>,<,==,<=,>=,!=3.测试运算:in,not i ...
- Cookie简单介绍
Cookie 饼干. 其实是一份小数据, 是服务器给客户端,并且存储在客户端上的一份小数据 应用场景 自动登录.浏览记录.购物车. 为什么要有这个Cookie http的请求是无状态. 客户端与服务器 ...
- JS高级学习笔记(9) 之 转:前端路由跳转基本原理
原文链接: 前端路由跳转基本原理 前述 前端三大框架Angular.React和Vue都推行单页面应用SPA开发模式,这是因为在路由切换时,替换DOM Tree中发生修改的DOM部分,来减少原来因为多 ...
- iOS延迟执行方法
swift 4.0中dispatch_async,dispatch_after的使用 2018年03月28日 16:15:44 xiao_yuly 阅读数:3576 版权声明:本文为博主原创文章,未经 ...
- 【TensorFlow】tf.reset_default_graph()函数
转载 https://blog.csdn.net/duanlianvip/article/details/98626111 tf.reset_default_graph函数用于清除默认图形堆栈并重置 ...