高性能内存队列Disruptor

导读:本篇文章讲解 高性能内存队列Disruptor,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、简介

Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。

基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能。

注意,这里所说的队列是系统内部的内存队列,而不是Kafka这样的分布式队列

Github:https://github.com/LMAX-Exchange/disruptor

Disruptor实现了队列的功能并且是一个有界队列,可以用于生产者-消费者模型

JUC包下队列存在的问题:

队列 描述
ArrayBlockingQueue 基于数组结构实现的一个有界阻塞队列
LinkedBlockingQueue 基于链表结构实现的一个无界阻塞队列,指定容量为有界阻塞队列
PriorityBlockingQueue 支持按优先级排序的无界阻塞队列
DelayQueue 基于优先级队列(PriorityBlockingQueue)实现的无界阻塞队列
SynchronousQueue 不存储元素的阻塞队列
LinkedTransferQueue 基于链表结构实现的一个无界阻塞队列
LinkedBlockingDeque 基于链表结构实现的一个双端阻塞队列
  • JUC下的队列大部分采用加ReentrantLock锁方式保证线程安全。在稳定性要求特别高的系统中,为了防止生产者速度过快,导致内存溢出,只能选择有界队列。
  • 加锁的方式通常会严重影响性能。线程会因为竞争不到锁而被挂起,等待其他线程释放锁而唤醒,这个过程存在很大的开销,而且存在死锁的隐患。
  • 有界队列通常采用数组实现。但是采用数组实现又会引发另外一个问题false sharing(伪共享)。

关于伪共享的介绍,可以参考《Java内存模型》中缓存一致性协议部分的内容,里面有对伪共享以及解决方案做详细的介绍

二、基本原理

2.1 设计方案

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

  • 环形数组结构

    为了避免垃圾回收,采用数组而非链表。同时,数组对处理器的缓存机制更加友好(空间局部性原理)。

  • 元素位置定位

    数组长度2^n,通过位运算,加快定位的速度(计算元素的索引下标)。每个元素都有一个序列号sequence,采取递增的形式。不用担心index溢出的问题。index是long类型,即使100万QPS的处理速度,也需要30万年才能用完。

  • 无锁设计

    每个生产者或者消费者线程,会先申请可以操作的元素在数组中的位置,申请到之后,直接在该位置写入或者读取数据。通过CAS和自旋的方式实现线程安全。

  • 利用缓存行填充结局了伪共享问题

  • 实现了基于事件驱动的生产者消费者模型(观察者模式)

    消费者时刻关注着队列里有没有消息,一旦有新消息产生,消费者线程就会立刻把它消费

2.2 RingBuffer数据结构

使用RingBuffer来作为队列的数据结构,RingBuffer就是一个可自定义大小的环形数组。除数组外还有一个序列号(sequence),用以指向下一个可用的元素,供生产者与消费者使用。原理图如下所示:

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/112104.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!