前言

最近工作比较忙,在工作项目中,看了很多人都自己实现了一套数据任务处理机制,个人感觉有点乱,且也方便他人的后续维护,所以想到了一种数据处理模式,即生产者、缓冲队列、消费者的模式来统一大家的实现逻辑。

下面时是对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的简单介绍与应用的更多相关文章

  1. [原创]关于mybatis中一级缓存和二级缓存的简单介绍

    关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...

  2. 利用Python进行数据分析(7) pandas基础: Series和DataFrame的简单介绍

    一.pandas 是什么 pandas 是基于 NumPy 的一个 Python 数据分析包,主要目的是为了数据分析.它提供了大量高级的数据结构和对数据处理的方法. pandas 有两个主要的数据结构 ...

  3. 利用Python进行数据分析(4) NumPy基础: ndarray简单介绍

    一.NumPy 是什么 NumPy 是 Python 科学计算的基础包,它专为进行严格的数字处理而产生.在之前的随笔里已有更加详细的介绍,这里不再赘述. 利用 Python 进行数据分析(一)简单介绍 ...

  4. yii2的权限管理系统RBAC简单介绍

    这里有几个概念 权限: 指用户是否可以执行哪些操作,如:编辑.发布.查看回帖 角色 比如:VIP用户组, 高级会员组,中级会员组,初级会员组 VIP用户组:发帖.回帖.删帖.浏览权限 高级会员组:发帖 ...

  5. angular1.x的简单介绍(二)

    首先还是要强调一下DI,DI(Denpendency Injection)伸手获得,主要解决模块间的耦合关系.那么模块是又什么组成的呢?在我看来,模块的最小单位是类,多个类的组合就是模块.关于在根模块 ...

  6. Linux的简单介绍和常用命令的介绍

    Linux的简单介绍和常用命令的介绍 本说明以Ubuntu系统为例 Ubuntu系统的安装自行百度,或者参考http://www.cnblogs.com/CoderJYF/p/6091068.html ...

  7. iOS-iOS开发简单介绍

    概览 终于到了真正接触IOS应用程序的时刻了,之前我们花了很多时间去讨论C语言.ObjC等知识,对于很多朋友而言开发IOS第一天就想直接看到成果,看到可以运行的IOS程序.但是这里我想强调一下,前面的 ...

  8. iOS开发多线程篇—多线程简单介绍

    iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...

  9. iOS开发UI篇—UITabBarController简单介绍

    iOS开发UI篇—UITabBarController简单介绍 一.简单介绍 UITabBarController和UINavigationController类似,UITabBarControlle ...

随机推荐

  1. ubuntu18.04.2 hadoop3.1.2+zookeeper3.5.5高可用完全分布式集群搭建

    ubuntu18.04.2 hadoop3.1.2+zookeeper3.5.5高可用完全分布式集群搭建 集群规划: hostname NameNode DataNode JournalNode Re ...

  2. 解决CGrIdCtrl合并单元格后继续拆分后的问题

    DWORD dwMergeCellCount = vMergeCells.size(); ; i < dwMergeCellCount; i++){ m_HFlexGrid.SplitCells ...

  3. 洛谷 P2458 [SDOI2006]保安站岗

    题目传送门 解题思路: 树形DP 可知一个点被控制有且仅有一下三种情况: 1.被父亲节点上的保安控制 2.被儿子节点上的保安控制 3.被当前节点上的保安控制 我们设dp[0/1/2][u]表示u节点所 ...

  4. BZOJ 3442 学习小组

    题解: 神建图 普通的二分图费用流建完后 添加学生x->t 容量为k-1的边 表示尽量让x参加一个活动,剩下的k-1次机会可以不参加 #include<iostream> #incl ...

  5. Eclipse字体及背景色设置和工作空间字符编码设置

    一.字体设置 Window->Preferences->General->Appearance->Colors and fonts->Basic->Text Fon ...

  6. NCRE的JAVA二级考试大纲

    全国计算机等级考试二级 Java 语言 程序设计考试大纲(2018 年版) 基本要求 1. 掌握 Java 语言的特点.实现机制和体系结构. 2. 掌握 Java 语言中面向对象的特性. 3. 掌握 ...

  7. vue移动端点击一个元素缩小,松手的时候元素恢复正常

    active伪类解决 HTML代码 <div class='box'> </div> CSS代码 .box { width: 100px; height: 100px; bac ...

  8. Sequence Models Week 1 Character level language model - Dinosaurus land

    Character level language model - Dinosaurus land Welcome to Dinosaurus Island! 65 million years ago, ...

  9. pytorch 自动求梯度

    自动求梯度 在深度学习中,我们经常需要对函数求梯度(gradient).PyTorch提供的autograd包能够根据输入和前向传播过程自动构建计算图,并执行反向传播.本节将介绍如何使用autogra ...

  10. JavaScript 之 Function

    JavaScript function 语句定义和用法: function 语句用于声明一个函数. 函数声明后,我们可以在需要的时候调用. 在 JavaScript 中,函数是对象,函数也有属性和方法 ...