目录
系列索引
简介
运行架构
核心内容
核心概念
核心优化点
具体优化对比
VS BlockingQueue
消除伪共享
内存预分配与无锁
系列索引
- Disruptor源码解析一 Disruptor高性能之道
- Disruptor源码解析二 Sequence相关类解析
- Disruptor源码解析三 RingBuffer解析
- Disruptor源码解析四 消费者的组织串联
- Disruptor源码解析五 消费者的具体实现
- Disruptor源码解析六 示例与性能压测
简介
LMAX Disruptor是一个高性能的线程间消息库。它源于LMAX对并发性,性能和非阻塞算法的研究,如今构成了Exchange基础架构的核心部分。
- Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。
- Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。
运行架构
架构图:
具体运行示例图:
一个生产者P1与三个消费者C1、C2、C3,C3的事件处理需要C1与C2先完成。则该模型结构如下:
核心内容
核心概念
- Ring Buffer
- 如其名,环形的缓冲区。曾经 RingBuffer 是 Disruptor 中的最主要的对象,但从3.0版本开始,其职责被简化为仅仅负责对通过 Disruptor 进行交换的数据(事件)进行存储和更新。在一些更高级的应用场景中,Ring Buffer 可以由用户的自定义实现来完全替代。
- Sequence Disruptor
- 通过顺序递增的序号来编号管理通过其进行交换的数据(事件),对数据(事件)的处理过程总是沿着序号逐个递增处理。一个 Sequence 用于跟踪标识某个特定的事件处理者( RingBuffer/Consumer )的处理进度。虽然一个 AtomicLong 也可以用于标识进度,但定义 Sequence 来负责该问题还有另一个目的,那就是防止不同的Sequence 之间的CPU缓存伪共享(Flase Sharing)问题。
- Sequencer Sequencer
- 是 Disruptor 的真正核心。此接口有两个实现类 SingleProducerSequencer、MultiProducerSequencer ,它们定义在生产者和消费者之间快速、正确地传递数据的并发算法。
- Sequence Barrier
- 用于保持对RingBuffer的 main published Sequence 和Consumer依赖的其它Consumer的 Sequence 的引用。 Sequence Barrier 还定义了决定 Consumer 是否还有可处理的事件的逻辑。
- Wait Strategy
- 定义 Consumer 如何进行等待下一个事件的策略。 (注:Disruptor 定义了多种不同的策略,针对不同的场景,提供了不一样的性能表现)
- Event
- 在 Disruptor 的语义中,生产者和消费者之间进行交换的数据被称为事件(Event)。它不是一个被 Disruptor 定义的特定类型,而是由 Disruptor 的使用者定义并指定。
- EventProcessor
- EventProcessor 持有特定消费者(Consumer)的 Sequence,并提供用于调用事件处理实现的事件循环(Event Loop)。
- EventHandler
- Disruptor 定义的事件处理接口,由用户实现,用于处理事件,是 Consumer 的真正实现。
- Producer
- 即生产者,只是泛指调用 Disruptor 发布事件的用户代码,Disruptor 没有定义特定接口或类型。
核心优化点
- 减少垃圾回收
- 让消息可以通过被多个消费者并行处理
- 使用无锁算法来实现并发(CAS)
- 缓存行填充
具体优化对比
VS BlockingQueue
- producer只能从head放数据,producer之间会竞争head指针,存在写竞争。
- consumer之间会竞争tail指针,它们之间也存在写竞争。
- 并且很多情况下,queue是处于全空状态,head/tail指针指向同一个entry,producer和consumer之间也存在写竞争。因此需要lock来实现synchronization。
- 另一个缺点是heal/tail指针的false sharing。
两个Queue的对比测试代码, 单生产者单消费者的情况下,disruptor的处理速度(1E数据约7100ms)约是blockingQueue(1E数据约21000ms)的3倍 ,Disruptor代码 :
public class DisruptorSingle4Test {
public static void main(String[] args) {
int ringBufferSize = 65536;
final Disruptor<Data> disruptor = new Disruptor<Data>(
new EventFactory<Data>() {
@Override
public Data newInstance() {
return new Data();
}
},
ringBufferSize,
Executors.newSingleThreadExecutor(),
ProducerType.SINGLE,
//new BlockingWaitStrategy()
new YieldingWaitStrategy()
);
DataConsumer consumer = new DataConsumer();
//消费数据
disruptor.handleEventsWith(consumer);
disruptor.start();
new Thread(new Runnable() {
@Override
public void run() {
RingBuffer<Data> ringBuffer = disruptor.getRingBuffer();
// 约1E
for (long i = 0; i < Integer.MAX_VALUE / 20; i++) {
long seq = ringBuffer.next();
Data data = ringBuffer.get(seq);
data.setId(i);
data.setName("c" + i);
ringBuffer.publish(seq);
}
}
}).start();
}
public static class DataConsumer implements EventHandler<Data> {
private long startTime;
private int i;
public DataConsumer() {
this.startTime = System.currentTimeMillis();
}
@Override
public void onEvent(Data data, long seq, boolean bool)
throws Exception {
i++;
if (i == Constants.EVENT_NUM_OHM) {
long endTime = System.currentTimeMillis();
System.out.println("Disruptor costTime = " + (endTime - startTime) + "ms");
}
}
}
// event
public static class Data implements Serializable {
private static final long serialVersionUID = 2035546038986494352L;
private Long id ;
private String name;
public Data() {
}
public Data(Long id, String name) {
super();
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
BlockingQueue代码 :
public class ArrayBlockingQueue4Test {
public static void main(String[] args) {
final ArrayBlockingQueue<Data> queue = new ArrayBlockingQueue<Data>(100000000);
final long startTime = System.currentTimeMillis();
//向容器中添加元素
new Thread(new Runnable() {
@Override
public void run() {
long i = 0;
// 约1E
while (i < Integer.MAX_VALUE / 20) {
Data data = new Data(i, "c" + i);
try {
queue.put(data);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
int k = 0;
// 约1E
while (k < Integer.MAX_VALUE / 20) {
try {
queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
k++;
}
long endTime = System.currentTimeMillis();
System.out.println("ArrayBlockingQueue costTime = " + (endTime - startTime) + "ms");
}
}).start();
}
}
消除伪共享
CPU缓存
- 由缓存行组成,通常是 64 字节(常用处理器的缓存行是 64 字节的,比较旧的处理器缓存行是 32 字节),并且它有效地引用主内存中的一块地址。一个 Java 的 long 类型是 8 字节,因此在一个缓存行中可以存 8 个 long 类型的变量。
缓存行对代码的性能影响对比, 2E数据的场景下,正常处理时间10.5s,消除伪共享处理时间1.5s,处理性能约是10倍,提升巨大。
public class FalseSharingTest {
public static void main(String[] args) throws InterruptedException {
testPointer(new Pointer());
}
private static void testPointer(Pointer pointer) throws InterruptedException {
long start = System.currentTimeMillis();
Thread t1 = new Thread(() -> {
for (int i = 0; i < Integer.MAX_VALUE / 10; i++) {
pointer.x++;
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < Integer.MAX_VALUE / 10; i++) {
pointer.y++;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(System.currentTimeMillis() - start);
System.out.println(pointer);
}
static class Pointer {
volatile long x;
// 打开这里的注释可以开启缓存行处理时长对比
//long p1, p2, p3, p4, p5, p6, p7;
volatile long y;
}
static class Pointer2 {
MyLong x = new MyLong();
MyLong y = new MyLong();
}
static class MyLong {
volatile long value;
long p1, p2, p3, p4, p5, p6, p7;
}
}
内存预分配与无锁
参考RingBuffer, 预先构造ringBuffer 内存对象。
利用Sequencer协调多个生产者,sequenceBarrier协调多个消费者,无锁并发。