【开发总结】Disruptor 使用简介

在极客时间看到王宝令老师关于 Disruptor 的一篇文章,觉得很有意思。看完之后又在网上找到一些其他关于Disruptor 的资料看了一下。

现在写篇文章总结一下。

使用

Disruptor 百度翻译是干扰者,分裂器的意思。

在这里它其实是一个高性能队列,一个queue。所以我有点想不通为什么名字取成这样。有清楚的同学可以知会我一生。

Disruptor 的使用相对Java集合类中的队列,会更加复杂。

第一步,引入jar包.

<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>

第二步,生成 Disruptor 对象

第三步,设置队列中消息消费的handler.

第四步,启动 Disruptor 线程。

第五步,获取ringbuffer。生产者通过向 Disruptor 的ringbuffer 来发布消息的。所以事先要先获取ringbuffer。

第六步,发布消息。

/**
* @description:
* @author: lkb
* @create: 2020-10-28 19:46
*/
@Slf4j
public class MyTest { public static void main(String[] args) {
//指定RingBuffer大小,
//必须是2的N次方
int bufferSize = 1024; //构建Disruptor
Disruptor<LongEvent> disruptor
= new Disruptor<>(
LongEvent::new,
bufferSize,
DaemonThreadFactory.INSTANCE); //注册事件处理器
disruptor.handleEventsWith(
(event, sequence, endOfBatch) ->
System.out.println("E: " + event)); //启动Disruptor
disruptor.start(); //获取RingBuffer
RingBuffer<LongEvent> ringBuffer
= disruptor.getRingBuffer();
//生产Event
ByteBuffer bb = ByteBuffer.allocate(8);
for (long l = 0; true; l++) {
bb.putLong(0, l);
//生产者生产消息
ringBuffer.publishEvent(
(event, sequence, buffer) ->
event.set(buffer.getLong(0)), bb);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log.error(e.getMessage(), e);
}
}
}
}

其中LongEvent 是一个普通的POJO对象

public class LongEvent {
private long value;
public void set(long value) {
this.value = value;
}
}

Disruptor 的使用是典型的生产者-消费者模式。

Java集合中的队列更符合我们对队列的操作习惯。以ArrayBlockingQueue为例,我们可以把ArrayBlockingQueue想象为一个队列管道,生产者线程生产完数据后,将数据丢到队列中,消费者线程从另外一端取出数据,进行消费。

而Disruptor 相对其他传统的队列而言更像一个“大家长”,生成者需要通过这位“大家长”的ringbuffer将消息发送出去,消费者需要将处理操作注册到“大家长”这里。

这样的队列操作不太符合我们的习惯,所以使用上会不那么顺手。

高效的秘诀

在不顺手的情况下,为什么还是有很多系统用到它呢?原因在于它非常高效。

上面是网上找到的性能对比图。可以看到Disruptor性能上是非常高的。

那它是如何实现高效的呢?

  1. 内存分配更加合理,使用 RingBuffer 数据结构,数组元素在初始化时一次性全部创建,提升缓存命中率;
  2. 对象循环利用,避免频繁 GC。
  3. 能够避免伪共享,提升缓存利用率。
  4. 采用无锁算法,避免频繁加锁、解锁的性能消耗。支持批量消费,消费者可以无锁方式消费多个消息。

对于第四点,相信大家都很清楚。锁操作涉及到操作系统状态切换,这个操作是非常耗时耗资源的。无锁操作可以避免状态切换。

对于前面三点,涉及到一个非常重要的概念,就是缓存。CPU有三级缓存。离CPU越近的缓存,速度越快,但是容量越小。因为CPU的速度远远大于其他硬件的速度,设置缓存能够减小CPU和其他硬件的速度差。这个缓存和生产者消费者中间的队列有异曲同工之妙。

为了提高缓存的命中率,硬件通过局部性原理,在加载一个数据的同时将它周围的数据也加载进去。

程序的局部性原理指的是在一段时间内程序的执行会限定在一个局部范围内。这里的“局部性”可以从两个方面来理解,一个是时间局部性,另一个是空间局部性。时间局部性指的是程序中的某条指令一旦被执行,不久之后这条指令很可能再次被执行;如果某条数据被访问,不久之后这条数据很可能再次被访问。而空间局部性是指某块内存一旦被访问,不久之后这块内存附近的内存也很可能被访问。

上诉的第1、2条,通过将数据设置进连续相邻的内存位置,CPU在读取了一个数据的时候,发现第二个数据已经因为“局部性”原理加载进缓存,就不需要再次去寻址,直接从缓存中获取数据。

第1,2条是对缓存的高效利用,第3条就是对缓存低效使用的规避。

有一种缓存低效使用的方式是“伪共享”。内存是按照缓存行进行管理的。缓存行的大小通常是64个字节。

例如一个缓存行存储了两个对象,对其中一个对象的操作会使得整个缓存行失效。也就是说即使对象B被加入了缓存,但是因为其他对象的操作无效了。

第3条中,Disruptor 中通过将对象包裹,让一个对象充满整个缓存行,避免了伪共享的问题。

还有一点就是,相对于其他阻塞队列,Disruptor 的等待策略更多,功能更加强大。

通过对缓存的利用和无锁操作,Disruptor 成为一个高效队列。

一些思考

Disruptor 的一些思想其实在其他框架上也是常见的。

避免伪共享问题上,MySQL 8.0 版本直接将查询缓存的整块功能删掉了;在高效利用缓存上,线程池、队列等都多算缓存概念的受益者;避免锁操作上,Java的底层的各种锁优化,也是利用这点,比如轻量级锁。

为什么这么多框架会不约而同地想到这些问题呢?

因为计算机、操作系统是非常成熟的,底层都是非常相似的架构。了解计算机底层原理,对这些知识才能触类旁通。所以,啥不说,计算机基础课,我打算再上一遍。

Disruptor 使用简介的更多相关文章

  1. Disruptor使用简介

    disruptor是lmax公司开发的一款java高性能并发框架,其本质是一种类似队列的实现“生产者—消费者 ”模式的组件. 下面是其示例代码: public class DisruptorServe ...

  2. Java深入学习(6):Disruptor

    Disruptor框架简介: 并发框架,基于事件驱动,使用观察者模式 底层采用环形数组,取模算法 简单使用: /** * 声明一个Event:表示生产者和消费者之间传递的数据类型 */ public ...

  3. Disruptor并发框架(一)简介&上手demo

    框架简介 Martin Fowler在自己网站上写了一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金融交易平台,它能够以很低的延迟产生大量交易.这个系统是建立在JVM平台上,其核心是一 ...

  4. Disruptor并发框架简介

    Martin Fowler在自己网站上写一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金额交易平台,它能够以很低的延迟产生大量交易.这个系统是建立在JVM平台上,其核心是一个业务逻辑处 ...

  5. disruptor 高并发编程 简介demo

    原文地址:http://www.cnblogs.com/qiaoyihang/p/6479994.html disruptor适用于大规模低延迟的并发场景.可用于读写操作分离.数据缓存,速度匹配(因为 ...

  6. 从构建分布式秒杀系统聊聊Disruptor高性能队列

    前言 秒杀架构持续优化中,基于自身认知不足之处在所难免,也请大家指正,共同进步.文章标题来自码友 简介 LMAX Disruptor是一个高性能的线程间消息库.它源于LMAX对并发性,性能和非阻塞算法 ...

  7. 无锁并发框架Disruptor学习入门

    刚刚听说disruptor,大概理一下,只为方便自己理解,文末是一些自己认为比较好的博文,如果有需要的同学可以参考. 本文目标:快速了解Disruptor是什么,主要概念,怎么用 1.Disrupto ...

  8. 01-项目简介Springboot简介入门配置项目准备

    总体课程主要分为4个阶段课程: ------------------------课程介绍------------------------ 01-项目简介Springboot简介入门配置项目准备02-M ...

  9. Disruptor 详解 一

    这篇博客将主要通过几个示例,简单讲述 Disruptor 的使用方法: 一.disruptor 简介 Disruptor 是英国外汇交易公司 LMAX 开发的一个无锁高性能的线程间消息传递的框架.目前 ...

随机推荐

  1. svn提交代码出错

    今天提交代码的时候一直报错,下面是错误信息 Error: Commit failed (details follow):  Error: Commit blocked by pre-commit ho ...

  2. Java中synchronized关键字理解

    好记性不如烂笔头~~ 并发编程中synchronized关键字的地位很重要,很多人都称它为重量级锁.利用synchronized实现同步的基础:Java中每一个对象都可以作为锁.具体表现为以下三种形式 ...

  3. RestTemplate get请求多参数 简单封装

    使用RestTemplate发送get请求时,如果有多个参数拼接起来会比较麻烦,在此做个简单的封装 public static void main(String[] args) { Map<St ...

  4. vue+less换肤,主题切换方案

    新的项目对于客户自定义要求很高,然后换肤是其中一个很小的模块,经过了一段时间的摸索,看了许多文章,找到了几种方案. https://www.cnblogs.com/leiting/p/11203383 ...

  5. linux 基础语法

    1.linux常用命令 1.1 系统命令 runlevel                     # 查看当前的运行级别systemctl status sshd        # 开启网络服务功能 ...

  6. python中random的一些用法

    #(1)随机小数 import random print(random.random())  #随机大于0 且小于1 之间的小数 ''' 0.9441832228391154 ''' print(ra ...

  7. Android和CraigDJ

    下载HTML source - 46.8 KB 下载APK - 8.1 MB Introduction  大家好,欢迎来到我的Android应用程序项目! 我必须承认,我仍然对原生安卓环境几乎一无所知 ...

  8. 2-K8S常用命令

    kubectl 命令行管理工具 类型 命令 描述 基础命令 create 通过文件名或标准输入创建资源 expose 为Deployment,Pod创建service run 在集群中运行一个特定的镜 ...

  9. CentOS7.7 系统下 virbr0 虚拟网卡的维护与管理

    在 CentOS 7 系统的安装过程中,如果有选择相关虚拟化的的服务安装系统后,启动网卡时会发现有一个以网桥连接的私网地址的 virbr0 网卡,这个是因为在虚拟化中有使用到 libvirtd 服务生 ...

  10. 设置 eclipse C++ 版本

    gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC) 默认是使用 C++ 98 版本进行编译 设置 eclipse 中 C++ 的版本: Project  -& ...