高性能队列:Disruptor介绍以及使用场景 (一)

最近看了一些关于Disruptor的介绍,觉得这个新的消息队列非常厉害,单机性能就非常高。于是搜索并整理了一些资料,寻找往后可能使用的场景。

Disruptor 介绍

Disruptor 是LMX开源出来的java编写的一个并发消息处理器,在队列中一边生产者放入消息,另外一边消费者并行取出处理,其核心是根据现代CPU硬件缓存特点发明不同于通用LinkedList或Queue的新型数据结构RingBuffer。

1)Disruptor 是英国外汇交易公司 LMAX 开发的一个高性能的并发框架。可以认为是线程间通信的高效低延时的内存消息组件,它最大的特点是高性能。与 Kafka、RabbitMQ 用于服务间的消息队列不同,disruptor 一般用于一个 JVM 中多个线程间消息的传递。
(2)从功能上来看,Disruptor 实现了“队列”的功能,而且是一个有界队列(事实上它是一个无锁的线程间通信库)。作用与 ArrayBlockingQueue 有相似之处,但是 disruptor 从功能、性能上又都远好于 ArrayBlockingQueue。

实际上这个框架在 log4j,以及 activeMQ 源码扩展中都有使用。(例如:由于采用了 Disruptor,Log4j 2 性能明显优于 Log4j 1.x,Logback 和 java.util.logging,尤其是在多线程应用程序中,异步记录器的吞吐量比 Log4j 1.x 和 Logback 高 18 倍,延迟低。)

高性能队列 Disruptor

LMX 系统每秒处理600万订单,这是个神话!,其业务模式是:In Memory+Event Source+Disruptor
Disruptor 框架解决了锁、伪共享带来的性能影响。

为什么队列干扰了缓存呢?解释是这样的: 为了将数据放入队列,你需要写入队列,类似地,为了从队列取出数据,你需要移除队列也是一种写,客户端也许不只一次写入同样数据结构,处理写通常需要锁,但是如果锁使用了,会引起切换到底层系统的场景(线程上下文切换—个人理解), 当这个发生后,处理器会丢失它的缓存中的数据。jdon

Disruptor 和BlockingQueue 的性能比较

(1)Disruptor 的性能比 BlockingQueue 提高了 5~10 倍左右
(2)也就是说 BlockingQueue 能做的,Disruptor 都能做到且做的更好。

Disruptor 能做得更多事情:
– 同一个“事件”可以有多个消费者,消费者之间既可以并行处理,也可以相互依赖形成处理的先后次序(形成一个依赖图);
– 预分配用于存储事件内容的内存空间;
– 针对极高的性能目标而实现的极度优化和无锁的设计;

Disruptor 性能高效的原因

(1)环形数组结构
– 为了避免垃圾回收,Disruptor 的 RingBuffer 采用数组而非链表。同时,数组对处理器的缓存机制更加友好。
(2)快速的元素位置定位策略
– 由于 RingBuffer 是环状队列结构,在初始化时需要指定大小(即这个环最多可以容纳多少槽)。而 Disruptor 规定了 RingBuffer 大小必须是 2 的 n 次方,这样通过位运算,可以加快定位的速度。
– 同时下标采取递增的形式。不用担心 index 溢出的问题。index 是 long 类型,即使 100 万 QPS 的处理速度,也需要 30 万年才能用完。
(3)无锁设计
– 在需要确保操作是线程安全的地方,Disruptor 并不使用用锁,而是使用 CAS(Compare And Swap/Set)操作。每个生产者或者消费者线程,会先申请可以操作的元素在数组中的位置,申请到之后,直接在该位置写入或者读取数据。

Disruptor 使用场景说明

任何一个技术方案都有个使用场景的Disruptor也不例外,此框架的使用场景总结如下:

补充个前提:那就是接到的消息量巨大,如果每秒不上万,十万,百万,那还是不要使用这个框架了,你的消费处理不要太延迟,如果你把这个框架用于往数据库写数据就不那么适当了。

1、处理CQRS架构风格的command命令(jdon framework底层也是用的disruptor)
2、接收消息并处理场景,如,短信,邮件发送服务
3、业务流水线模式
4、队列使用场景也是Disruptor使用,反过来则未必

(完)

发表评论

邮箱地址不会被公开。 必填项已用*标注