上一章 DSL相关实战,本文为了直观地感受 Disruptor 有多快,设计了一个性能对比测试:Producer 发布 100 万次事件,从发布第一个事件开始计时,捕捉 Consumer 处理完所有事件的耗时。
测试用例在 Producer 如何将事件通知到 Consumer 的实现方式上,设计了三种不同的实现:
- Producer 的事件发布和 Consumer 的事件处理都在同一个线程,Producer 发布事件后立即触发 Consumer 的事件处理;
- Producer 的事件发布和 Consumer 的事件处理在不同的线程,通过 ArrayBlockingQueue 传递给 Consumer 进行处理;
- Producer 的事件发布和 Consumer 的事件处理在不同的线程,通过 Disruptor 传递给 Consumer 进行处理;
此次测试用例仅做了只有一个 Producer 和一个 Consumer 的情形,测试用例的代码如下:
CounterTracer tracer = tracerFactory.newInstance(DATA_COUNT);//计数跟踪到达指定的数值;
TestHandler handler = new TestHandler(tracer);//Consumer 的事件处理;
EventPublisher publisher = publisherFactory.newInstance(new PublisherCreationArgs(DATA_COUNT, handler));//通过工厂对象创建不同的 Producer 的实现;
publisher.start();
tracer.start();
//发布事件;
for (int i = 0; i < DATA_COUNT; i++) {
publisher.publish(i);
}
//等待事件处理完成;
tracer.waitForReached();
publisher.stop();
//输出结果;
printResult(tracer);
事件处理的实现只是调用一个计数器(CounterTracer)加1,该计数器跟踪从开始到达到总的事件次数时所耗的时间。
public class TestHandler {
private CounterTracer tracer;
public TestHandler(CounterTracer tracer) {
this.tracer = tracer;
}
/**
* 如果返回 true,则表示处理已经全部完成,不再处理后续事件;
*
* @param event
* @return
*/
public boolean process(TestEvent event){
return tracer.count();
}
}
针对单一Producer 和单一 Consumer 的测试场景,CounterTracer 的实现如下:
/**
* 测试结果跟踪器,计数器不是线程安全的,仅在单线程的 consumer 测试中使用;
*
* @author haiq
*
*/
public class SimpleTracer implements CounterTracer {
private long startTicks;
private long endTicks;
private long count = 0;
private boolean end = false;
private final long expectedCount;
private CountDownLatch latch = new CountDownLatch(1);
public SimpleTracer(long expectedCount) {
this.expectedCount = expectedCount;
}
@Override
public void start() {
startTicks = System.currentTimeMillis();
end = false;
}
@Override
public long getMilliTimeSpan() {
return endTicks - startTicks;
}
@Override
public boolean count() {
if (end) {
return end;
}
count++;
end = count >= expectedCount;
if (end) {
endTicks = System.currentTimeMillis();
latch.countDown();
}
return end;
}
@Override
public void waitForReached() throws InterruptedException {
latch.await();
}
}
第一种 Producer 的实现:直接触发事件处理;
public class DirectingPublisher implements EventPublisher {
private TestHandler handler;
private TestEvent event = new TestEvent();
public DirectingPublisher(TestHandler handler) {
this.handler = handler;
}
@Override
public void publish(int data) throws Exception {
event.setValue(data);
handler.process(event);
}
//省略其它代码;
}
第二种 Producer 的实现:通过 ArrayBlockinigQueue 实现;
public class BlockingQueuePublisher implements EventPublisher {
private ArrayBlockingQueue<TestEvent> queue ;
private TestHandler handler;
public BlockingQueuePublisher(int maxEventSize, TestHandler handler) {
this.queue = new ArrayBlockingQueue<TestEvent>(maxEventSize);
this.handler = handler;
}
public void start(){
Thread thrd = new Thread(new Runnable() {
@Override
public void run() {
handle();
}
});
thrd.start();
}
private void handle(){
try {
TestEvent evt ;
while (true) {
evt = queue.take();
if (evt != null && handler.process(evt)) {
//完成后自动结束处理线程;
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void publish(int data) throws Exception {
TestEvent evt = new TestEvent();
evt.setValue(data);
queue.put(evt);
}
//省略其它代码;
}
第三种 Producer 的实现:通过 Disruptor 实现;
public class DisruptorPublisher implements EventPublisher {
private class TestEventHandler implements EventHandler<TestEvent> {
private TestHandler handler;
public TestEventHandler(TestHandler handler) {
this.handler = handler;
}
@Override
public void onEvent(TestEvent event, long sequence, boolean endOfBatch)
throws Exception {
handler.process(event);
}
}
private static final WaitStrategy YIELDING_WAIT = new YieldingWaitStrategy();
private Disruptor<TestEvent> disruptor;
private TestEventHandler handler;
private RingBuffer<TestEvent> ringbuffer;
private ExecutorService executor;
public DisruptorPublisher(int bufferSize, TestHandler handler) {
this.handler = new TestEventHandler(handler);
executor = Executors.newSingleThreadExecutor();
disruptor = new Disruptor<TestEvent>(EVENT_FACTORY, bufferSize,
executor, ProducerType.SINGLE,
YIELDING_WAIT);
}
@SuppressWarnings("unchecked")
public void start() {
disruptor.handleEventsWith(handler);
disruptor.start();
ringbuffer = disruptor.getRingBuffer();
}
@Override
public void publish(int data) throws Exception {
long seq = ringbuffer.next();
try {
TestEvent evt = ringbuffer.get(seq);
evt.setValue(data);
} finally {
ringbuffer.publish(seq);
}
}
//省略其它代码;
}
Producer 第一种实现并没有线程间的交换,实际上就是直接调用计数器,因此以此种实现的测试结果作为基准,对比其它的两种实现的测试结果。
在我的CPU CORE i5 / 4G 内存 / Win7 64 位的笔记本上,数据量(DATA_COUNT)取值为 1024 * 1024 时的测试结果如下:
【基准测试】
[1]--每秒吞吐量:--;(1048576/0ms)
[2]--每秒吞吐量:--;(1048576/0ms)
[3]--每秒吞吐量:--;(1048576/0ms)
[4]--每秒吞吐量:69905066;(1048576/15ms)
[5]--每秒吞吐量:--;(1048576/0ms)
【对比测试1: ArrayBlockingQueue 实现】
[1]--每秒吞吐量:4788018;(1048576/219ms)
[2]--每秒吞吐量:5165399;(1048576/203ms)
[3]--每秒吞吐量:4809981;(1048576/218ms)
[4]--每秒吞吐量:5165399;(1048576/203ms)
[5]--每秒吞吐量:5577531;(1048576/188ms)
【对比测试2: Disruptor实现】
[1]--每秒吞吐量:33825032;(1048576/31ms)
[2]--每秒吞吐量:65536000;(1048576/16ms)
[3]--每秒吞吐量:65536000;(1048576/16ms)
[4]--每秒吞吐量:69905066;(1048576/15ms)
[5]--每秒吞吐量:33825032;(1048576/31ms)
从测试结果看, Disruptor 的性能比 ArrayBlockingQueue 高出了几乎一个数量级,操作耗时也只有平均20毫秒左右。