本次代码测试基于相同的 容量、生产者线程、单个生产者生产量、单个消费线程。
测试结果:
经测试证明:
- 在容量达到阀值时, Disruptor的性能更突出
- RingBuffer 的大小 与 消费者的消费速度 都直接影响到整个耗时。
测试入口
package com.lmax.disruptor.noob;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
public class CompareTest {
public static int THREAD = 2 << 10; // 线程数量
public static int PER = 2 << 10; // 单个线程生产数量
public static int CAP = 256; // 最大容量
public static void main(String[] args) {
println("生产线程数:" + THREAD + " 单个线程生产量: " + PER + " 最大容量:" + CAP);
new Thread(() -> ArrayBlockingQueueTest.execute()).start();
new Thread(() -> DisruptorTest.execute()).start();
}
public static void println(String msg) {
System.out.println(DateTimeFormatter.ISO_INSTANT.format(Instant.now()) + " " + msg);
}
}
ArrayBlockingQueue 测试用例
package com.lmax.disruptor.noob;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
public class ArrayBlockingQueueTest {
public static void execute() {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(CompareTest.CAP);
AtomicLong count = new AtomicLong();
AtomicBoolean endP = new AtomicBoolean(false);
AtomicBoolean endC = new AtomicBoolean(false);
long startTime = System.currentTimeMillis();
for (int i = 0; i < CompareTest.THREAD; i++) {
final int m = i;
new Thread(() -> {
for (int j = 0; j < CompareTest.PER; j++) {
queue.offer("i" + m + "j" + j);
count.incrementAndGet();
if (count.get() == CompareTest.CAP) {
CompareTest.println("ArrayBlockingQueue 生产耗时:" + (System.currentTimeMillis() - startTime));
endP.set(true);
}
}
}).start();
}
new Thread(() -> {
while (!queue.isEmpty() && count.get() != CompareTest.CAP) {
queue.poll();
}
CompareTest.println("ArrayBlockingQueue 消费耗时:" + (System.currentTimeMillis() - startTime));
endC.set(true);
}).start();
while (!(endC.get() && endP.get())) {
}
CompareTest.println("ArrayBlockingQueue 总耗时:" + (System.currentTimeMillis() - startTime));
}
}
Disruptor 测试用例
验证要点
- 因为 当RingBuffer没有空闲空间时,ringBuffer.next()会阻塞生产者的获取sequence。 如果槽的个数是2的N次方更有利于基于二进制的计算机进行计算。
(校对注:2的N次方换成二进制就是1000,100,10,1这样的数字, sequence & (array length-1) = array index,
比如一共有8槽,3&(8-1)=3,HashMap就是用这个方式来定位数组元素的,这种方式比取模的速度更快。) - Disruptor 消费与生产是完全异步的。
- 如果我们使用RingBuffer.next()获取一个事件槽,那么一定要发布对应的事件。如果不能发布事件,那么就会引起Disruptor状态的混乱
- Disruptor 事件处理handler必须在启动之前绑定。 handleEventsWith时子过程Disruptor.checkNotStarted()会判定Disruptor已启动则报错
- handleEventsWith 会将EventHandler封装成EventProcessors<Runnable>, 在start()时,将这个BatchEventProcessor用传入的ThreadFactory封装, 扔入默认的BasicExecutor执行
- 消费者线程由初始化Disruptor时指定的threadFactory创建的
package com.lmax.disruptor.noob;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
public class DisruptorTest {
public static void execute() {
int bufferSize = CompareTest.CAP;
Disruptor<DataEvent> disruptor = new Disruptor<DataEvent>(new DataEventFactory(), bufferSize,
new ThreadFactory() {
@Override
public Thread newThread(Runnable eventProcessor) {
CompareTest.println("EventProcessor wrapper");// 对事件处理总线的封装
Thread thread = new Thread(eventProcessor);
thread.setName("EventProcessorWrapper");
return thread;
}
});
/**
* 创建EventProcessors<Runnable>.
* 子过程Disruptor.checkNotStarted()事件处理handler必须在启动之前绑定.
*/
disruptor.handleEventsWith(new DataEventHandler());
/**
* 将BatchEventProcessor用传入的ThreadFactory封装, 扔入默认的BasicExecutor执行
*/
disruptor.start();
CompareTest.println("disruptor start success!");
RingBuffer<DataEvent> ringBuffer = disruptor.getRingBuffer();
DataProducer producer = new DataProducer(ringBuffer);
DataEventProducerWithTranslator translator = new DataEventProducerWithTranslator(ringBuffer);
long start = System.currentTimeMillis();
AtomicLong count = new AtomicLong();
for (int l = 0; l < CompareTest.THREAD; l++) {
new Thread(() -> {
for (int m = 0; m < CompareTest.PER; m++) {
producer.onData(start);
// translator.onData(start);
count.getAndIncrement();
}
}).start();
}
}
}
事件
package com.lmax.disruptor.noob;
/**
* 事件实例封装 业务数据传递对象
*
* @author admin
*
*/
public class DataEvent {
private long startTime;
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
}
---
package com.lmax.disruptor.noob;
import com.lmax.disruptor.EventFactory;
/*
* 构建传递的数据封装对象, 在初始化ringBuffer时,直接给entries[]每个地址上初始化DataEvent
*/
public class DataEventFactory implements EventFactory {
@Override
public Object newInstance() {
return new DataEvent();
}
}
生产者
package com.lmax.disruptor.noob;
import com.lmax.disruptor.RingBuffer;
public class DataProducer {
private final RingBuffer<DataEvent> ringBuffer;
public DataProducer(RingBuffer<DataEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
}
/**
* 当前还是生产线程
* <p>
* onData用来发布事件,每调用一次就发布一次事件事件 它的参数会通过事件传递给消费者
*
* @param data
*/
public void onData(long data) {//
// 可以把ringBuffer看做一个事件队列,那么next就是得到下面一个事件槽, 若没有空闲的时间槽则阻塞
long sequence = ringBuffer.next();
// CompareTest.println("生产置入sequence:" + sequence);
try {
// 用上面的索引取出一个空的事件用于填充
DataEvent event = ringBuffer.get(sequence);// for the sequence
event.setStartTime(data);
} finally {
// 发布事件
ringBuffer.publish(sequence);
}
}
}
获取下一个事件槽并发布事件要使用try/finally保证事件一定会被发布, 所以最好直接使用 ringBuffer.publishEvent方式将数据交由Translator来处理填充DataEvent,最后finally发布
package com.lmax.disruptor.noob;
import com.lmax.disruptor.EventTranslatorOneArg;
import com.lmax.disruptor.RingBuffer;
/**
* 获取下一个事件槽并发布事件(发布事件的时候要使用try/finnally保证事件一定会被发布)。
* 如果我们使用RingBuffer.next()获取一个事件槽,那么一定要发布对应的事件。如果不能发布事件,那么就会引起Disruptor状态的混乱
* 。尤其是在多个事件生产者的情况下会导致事件消费者失速,从而不得不重启应用才能会恢复。
*
* @author admin
*
*/
public class DataEventProducerWithTranslator {
private final RingBuffer<DataEvent> ringBuffer;
// 一个translator可以看做一个事件初始化器,publicEvent方法会调用它
// 填充Event
private static final EventTranslatorOneArg<DataEvent, Long> TRANSLATOR = new EventTranslatorOneArg<DataEvent, Long>() {
public void translateTo(DataEvent event, long sequence, Long startTime) {
event.setStartTime(startTime);
}
};
public DataEventProducerWithTranslator(RingBuffer<DataEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void onData(Long bb) {
ringBuffer.publishEvent(TRANSLATOR, bb);
// 当前还是生产者线程
// CompareTest.println(Thread.currentThread().getName() + " pulishEvent end!");
}
}
处理者
package com.lmax.disruptor.noob;
import java.util.concurrent.atomic.AtomicLong;
import com.lmax.disruptor.EventHandler;
/**
* 对指定事件的处理过程
*
*/
public class DataEventHandler implements EventHandler<DataEvent> {
public AtomicLong count = new AtomicLong(0);
@Override
public void onEvent(DataEvent event, long sequence, boolean endOfBatch) throws Exception {
/**
* 消费者线程由初始化Disruptor时指定的threadFactory创建的
*/
// CompareTest.println(Thread.currentThread().getName() + "----" + event.getStartTime());
count.incrementAndGet();
if (count.incrementAndGet() == CompareTest.CAP) {
CompareTest.println("处理的sequence:" + sequence + " Disruptor 总耗时:"
+ (System.currentTimeMillis() - event.getStartTime()));
}
}
}