原文地址: haifeiWu和他朋友们的博客
博客地址:www.hchstudio.cn
欢迎转载,转载请注明作者及出处,谢谢!

最近一直在研究队列的一些问题,今天楼主要分享一个高性能的队列 Disruptor 。

what Disruptor ?

它是英国外汇交易公司 LMAX 开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题。基于 Disruptor 开发的系统单线程能支撑每秒600万订单。

目前,包括 Apache Storm、Log4j2 在内的很多知名项目都应用了Disruptor以获取高性能。在楼主公司内部使用 Disruptor 与 Netty 结合用来做 GPS 实时数据的处理,性能相当强悍。本文从实战角度来大概了解一下 Disruptor 的实现原理。

why Disruptor ?

Disruptor通过以下设计来解决队列速度慢的问题:

  • 环形数组结构

    为了避免垃圾回收,采用数组而非链表。因为,数组对处理器的缓存机制更加友好。
  • 元素位置定位

    数组长度2^n,通过位运算,加快定位的速度。下标采取递增的形式。不用担心index溢出的问题。index是long类型,即使100万QPS的处理速度,也需要30万年才能用完。
  • 无锁设计

    每个生产者或者消费者线程,会先申请可以操作的元素在数组中的位置,申请到之后,直接在该位置写入或者读取数据。
  • 针对伪共享问题的优化

    Disruptor 消除这个问题,至少对于缓存行大小是64字节或更少的处理器架构来说是这样的(有可能处理器的缓存行是128字节,那么使用64字节填充还是会存在伪共享问题),通过增加补全来确保ring buffer的序列号不会和其他东西同时存在于一个缓存行中。

how Disruptor ?

通过上面的介绍,我们大概可以了解到 Disruptor 是一个高性能的无锁队列,那么该如何使用呢,下面楼主通过 Disruptor 实现一个简单的生产者消费者模型,介绍 Disruptor 的使用

首先,根据 Disruptor 的事件驱动的编程模型,我们需要定义一个事件来携带数据。


public class DataEvent {
private long value; public void set(long value) {
this.value = value;
} public long getValue() {
return value;
}
}

为了让 Disruptor 为我们预先分配这些事件,我们需要构造一个 EventFactory 来执行构造


public class DataEventFactory implements EventFactory<DataEvent> { @Override
public DataEvent newInstance() {
return new DataEvent();
}
}

一旦我们定义了事件,我们需要创建一个处理这些事件的消费者。 在我们的例子中,我们要做的就是从控制台中打印出值。

public class DataEventHandler implements EventHandler<DataEvent> {
@Override
public void onEvent(DataEvent dataEvent, long l, boolean b) throws Exception {
new DataEventConsumer(dataEvent);
}
}

接下来我们需要初始化 Disruptor ,并定义一个生产者来生成消息


public class DisruptorManager { private final static Logger LOG = LoggerFactory.getLogger(DisruptorManager.class); /*消费者线程池*/
private static ExecutorService threadPool;
private static Disruptor<DataEvent> disruptor;
private static RingBuffer<DataEvent> ringBuffer; private static AtomicLong dataNum = new AtomicLong(); public static void init(EventHandler<DataEvent> eventHandler) { //初始化disruptor
threadPool = Executors.newCachedThreadPool();
disruptor = new Disruptor<>(new DataEventFactory(), 8 * 1024, threadPool, ProducerType.MULTI, new BlockingWaitStrategy()); ringBuffer = disruptor.getRingBuffer();
disruptor.handleEventsWith(eventHandler);
disruptor.start(); new Timer().schedule(new TimerTask() {
@Override
public void run() {
LOG.info("放入队列中数据编号{},队列剩余空间{}", dataNum.get(), ringBuffer.remainingCapacity());
}
}, new Date(), 60 * 1000);
} /**
*
* @param message
*/
public static void putDataToQueue(long message) {
if (dataNum.get() == Long.MAX_VALUE) {
dataNum.set(0L);
} // 往队列中加事件
long next = ringBuffer.next();
try {
ringBuffer.get(next).set(message);
dataNum.incrementAndGet();
} catch (Exception e) {
LOG.error("向RingBuffer存入数据[{}]出现异常=>{}", message, e.getStackTrace());
} finally {
ringBuffer.publish(next);
}
} public static void close() {
threadPool.shutdown();
disruptor.shutdown();
}
}

最后我们来定义一个 Main 方法来执行代码


public class EventMain { public static void main(String[] args) throws Exception {
DisruptorManager.init(new DataEventHandler());
for (long l = 0; true; l++) {
DisruptorManager.putDataToQueue(l);
Thread.sleep(1000);
}
}
}

上面代码具体感兴趣的小伙伴请移步 https://github.com/haifeiWu/disruptor-learn

然后我们可以看到控制台打印出来的数据

小结

Disruptor 通过精巧的无锁设计实现了在高并发情形下的高性能。

另外在Log4j 2中的异步模式采用了Disruptor来处理。在这里楼主遇到一个小问题,就是在使用Log4j 2通过 TCP 模式往 logstash 发日志数据的时候,由于网络问题导致链接中断,从而导致 Log4j 2 不停的往 ringbuffer 中写数据,ringbuffer数据没有消费者,导致服务器内存跑满。解决方案是设置 Log4j 2 中 Disruptor 队列有界,或者换成 UDP 模式来写日志数据(如果数据不重要的话)。

参考链接

高性能无锁队列 Disruptor 初体验的更多相关文章

  1. DIOCP开源项目-Delphi高性能无锁队列(lock-free)

    最近想在DIOCP中加入任务调度线程,DIOCP的工作线程作为生产者(producer)将接受到的数据对象,投递到任务调度线程中,然后统一进行分配.然而这一切都需要一个队列, 这几天都在关注无锁队列. ...

  2. 基于无锁队列和c++11的高性能线程池

    基于无锁队列和c++11的高性能线程池线程使用c++11库和线程池之间的消息通讯使用一个简单的无锁消息队列适用于linux平台,gcc 4.6以上   标签: <无>   代码片段(6)[ ...

  3. EasyDarwin开源流媒体服务器高性能设计之无锁队列

    本文来自EasyDarwin团队Fantasy(fantasy(at)easydarwin.org) 一. EasyDarwin任务队列实现 EasyDarwin的任务队列是通过OSQueue类来组织 ...

  4. boost 无锁队列

    一哥们翻译的boost的无锁队列的官方文档 原文地址:http://blog.csdn.net/great3779/article/details/8765103 Boost_1_53_0终于迎来了久 ...

  5. 无锁队列以及ABA问题

    队列是我们非常常用的数据结构,用来提供数据的写入和读取功能,而且通常在不同线程之间作为数据通信的桥梁.不过在将无锁队列的算法之前,需要先了解一下CAS(compare and swap)的原理.由于多 ...

  6. HashMap的原理与实 无锁队列的实现Java HashMap的死循环 red black tree

    http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html https://zh.wikipedia.org/wiki/%E7%BA ...

  7. zeromq源码分析笔记之无锁队列ypipe_t(3)

    在上一篇中说到了mailbox_t的底层实际上使用了管道ypipe_t来存储命令.而ypipe_t实质上是一个无锁队列,其底层使用了yqueue_t队列,ypipe_t是对yueue_t的再包装,所以 ...

  8. 一个可无限伸缩且无ABA问题的无锁队列

    关于无锁队列,详细的介绍请参考陈硕先生的<无锁队列的实现>一文.然进一步,如何实现一个不限node数目即能够无限伸缩的无锁队列,即是本文的要旨. 无锁队列有两种实现形式,分别是数组与链表. ...

  9. 无锁队列--基于linuxkfifo实现

    一直想写一个无锁队列,为了提高项目的背景效率. 有机会看到linux核心kfifo.h 原则. 所以这个实现自己仿照,眼下linux我们应该能够提供外部接口. #ifndef _NO_LOCK_QUE ...

随机推荐

  1. android学习-Eclipse中修改Android项目图标

    参考原文:http://blog.csdn.net/wpwbb510582246/article/details/52556753 方法一:替换res文件夹下的ic_launcher-web.png图 ...

  2. 读书笔记 Week7 2018-4-19

    <构建之法> 第十二章 用户体验 读书笔记 首先不得不说,现如今大部分的电脑使用者,都被微软的图形化界面把口味养刁了.当然,包括我自己.无论是在微机原理上级的时候使用那些带着浓郁的上世纪八 ...

  3. 对加密的了解(DES/3DES/AES区别 )

    DES 1977年1月,美国政府颁布:采纳IBM公司设计的方案作为非机密数据的正式. 目前在国内,随着三金工程尤其是金卡工程的启动,DES算法在POS.ATM.磁卡及智能卡(IC卡).加油站.高速公路 ...

  4. java-tip-关于StringBuilder的使用

    当我们需要拼接字符串时,通常会使用StringBuilder,这里简单分析下StringBuilder的内部结构. StringBuilder内部是一个char数组,当调用append方法连接字符串时 ...

  5. Python中装饰器(转)

    本文由 伯乐在线 - 7even 翻译,艾凌风 校稿.未经许可,禁止转载!英文出处:Simeon Franklin.欢迎加入翻译组. 好吧,我标题党了.作为 Python 教师,我发现理解装饰器是学生 ...

  6. Html5新瓶装老酒之一--Touch事件处理

    移动端的应用越来越多的开始采用html5来实现的.Html5有许多新特性需要开发者注意,比如css3,touch事件等等.比如做一个轮播图,分析其实现要领,有三点: 1.图片的轮播效果对应的css 样 ...

  7. 49. Group Anagrams同义词合并

    [抄题]: Given an array of strings, group anagrams together. Example: Input: ["eat", "te ...

  8. http协议简析(一)

    HTTP:hype-text transfer protocol,超文本传输协议,超文本(html)在网络间(电脑与电脑之间)传输过程中所遵循的一些规则. 两台电脑之间要实现数据传输的条件 1.两台电 ...

  9. 908D New Year and Arbitrary Arrangement

    传送门 分析 代码 #include<iostream> #include<cstdio> #include<cstring> #include<string ...

  10. 认识Web前端、Web后端、桌面app和移动app新开发模式 - 基于Node.js环境和VS Code工具

    认识Web.桌面和移动app新开发模式 - 基于Node.js环境和VS Code工具 一.开发环境的搭建(基于win10) 1.安装node.js和npm 到node.js官网下载安装包(包含npm ...