传统队列和disruptor比较

传统队列

目前Java提供的消息队列特点如下图:
传统队列和disruptor比较

传统队列性能瓶颈

队列的数据结构一般分数组、链表、堆。基于堆的队列是由于优先级问题设计的,这里不考虑。
首先*的队列,这种对于系统来说不稳定,万一生产者比消费者快得多,很容易导致内存溢出。而加锁的队列会严重影响性能。因此,一种无锁、有界的环状队列disruptor出现了。

disruptor

背景

Disruptor是英國外匯交易公司LMAX開發的一個高性能隊列,研發的初衷是解決內存隊列的延遲問題(在性能測試中發現竟然與I/O操作處於同樣的數量級)。基於Disruptor開發的系統單線程能支撐每秒600萬訂單。

高性能的原因

环状队列
disruptor内部使用环形队列ringBuffer不会增加GC回收频率。

解决伪共享问题
伪共享是由于缓存一致性协议导致的。disruptor采用填充7个long型变量来保证cursor不会和其他数据处于同一个缓存行(多数计算机缓存行大小64字节),避免由于伪共享问题导致重新从主存读取数据。

位运算快速定位数组下标
设置环状数组的长度为2^n,这样可以采用seq & length的方式快速得到数组下标。

无锁设计
disruptor使用CAS保证线程安全,避免了锁带来的线程挂起/唤醒开销。

disruptor基本概念

RingBuffer:Ringbuffer是一个环状数组,用于存放数据,是disruptor的核心。

Sequencer:序号管理器,使得消费者和生产者可以正确读取、存储数据。

Sequence:消费者和生产者都会维护一份自己的序号,用于标记下一读写位置以及可读写区间。

Event:存储的数据是包装在Even中的,RingBuffer真正存的是Even。

EventHandler:自定义消费者接口,拿到消息后完成实际业务功能。

Producer:生产者接口,生成消息。

disruptor工作原理

单生产者写数据:

  1. 申請寫入m個元素;
  2. 若是有m個元素可以写,則返回写入后最大的序列號。这里需要判断是否有未读元素,disruptor会维护一份和ringBuffer大小相同的队列available Buffer,当ringBuffer被消费后,会在available Buffer对应的位置设置标志位-1,表示已经被消费了。当往ringBuffer添加元素时,会查看available Buffer对应位置是否被消费,如果已经被消费,就覆盖上面的元素。
  3. 通过CAS设置最大可写***,如果成功,开始写入。

多生产在写数据:
1、申请写入m个元素
2、如果有m个空位可以写入,返回最大可写***,每个生产者被分配一段可写空间
3、写入元素后,设置available Buffer里面对应的值,标记数据已更新。

消费者读数据:
1、什么m个可读数据
2、若是有m个可读,返回最大可读***
3、通过CAS设置新的读取***,若成功,开始读取数据