Disruptor (3)

扶醉桌前 提交于 2019-11-30 20:43:36

本次代码测试基于相同的 容量、生产线程数、单个线程生产量, 仅有一个消费线程。

修改各参数得到的结果:

数据规模、并发线程数、 最主要的是容量小时:Disruptor没有优势

2019-08-29T07:42:35.235Z  线程数:64 单线程生产量: 2048 容量:32 数据总量:131072
2019-11-26T07:57:42.087Z  dataEvent newInstance: 1
2019-11-26T07:57:42.087Z  dataEvent newInstance: 2
...
2019-11-26T07:57:42.087Z  dataEvent newInstance: 64
2019-08-29T07:42:48.742Z  EventProcessor wrapper
2019-08-29T07:42:48.743Z  disruptor start success!
2019-08-29T07:42:51.113Z  处理的sequence:131071 count:131072  Disruptor 总耗时:2369

2019-08-29T07:42:36.200Z  ArrayBlockingQueue 生产耗时:962
2019-08-29T07:42:36.200Z  处理count:131072  ArrayBlockingQueue 消费耗时:962
2019-08-29T07:42:36.201Z  ArrayBlockingQueue 总耗时:963
2019-08-29T08:24:38.641Z  线程数:512 单线程生产量: 2048 容量:32 数据总量:1048576
2019-08-29T08:24:38.670Z  EventProcessor wrapper
2019-08-29T08:24:38.670Z  disruptor start success!
2019-08-29T08:25:08.590Z  处理的sequence:1048575 count:1048576  Disruptor 总耗时:29918

2019-08-29T08:25:54.753Z  处理count:1048576  ArrayBlockingQueue 消费耗时:9231
2019-08-29T08:25:54.753Z  ArrayBlockingQueue 生产耗时:9230
2019-08-29T08:25:54.753Z  ArrayBlockingQueue 总耗时:9231

增大容量:  Disruptor的性能上升

2019-08-29T07:40:28.980Z  线程数:64 单线程生产量: 2048 容量:128 数据总量:131072
2019-08-29T07:40:29.008Z  EventProcessor wrapper
2019-08-29T07:40:29.008Z  disruptor start success!
2019-08-29T07:40:29.694Z  处理的sequence:131071 count:131072  Disruptor 总耗时:685

2019-08-29T07:47:42.436Z  处理count:131072  ArrayBlockingQueue 消费耗时:508
2019-08-29T07:47:42.436Z  ArrayBlockingQueue 生产耗时:508
2019-08-29T07:47:42.436Z  ArrayBlockingQueue 总耗时:508
2019-08-29T07:43:39.073Z  线程数:64 单线程生产量: 2048 容量:512 数据总量:131072
2019-08-29T07:43:39.101Z  EventProcessor wrapper
2019-08-29T07:43:39.101Z  disruptor start success!
2019-08-29T07:43:39.269Z  处理的sequence:131071 count:131072  Disruptor 总耗时:167

2019-08-29T07:43:53.722Z  ArrayBlockingQueue 生产耗时:383
2019-08-29T07:43:53.722Z  处理count:131072  ArrayBlockingQueue 消费耗时:383
2019-08-29T07:43:53.722Z  ArrayBlockingQueue 总耗时:383
2019-08-29T07:44:05.995Z  线程数:64 单线程生产量: 2048 容量:1024 数据总量:131072
2019-08-29T08:18:10.426Z  EventProcessor wrapper
2019-08-29T08:18:10.426Z  disruptor start success!
2019-08-29T08:18:10.524Z  处理的sequence:131071 count:131072  Disruptor 总耗时:97

2019-08-29T07:44:06.365Z  ArrayBlockingQueue 生产耗时:367
2019-08-29T07:44:06.365Z  处理count:131072  ArrayBlockingQueue 消费耗时:367
2019-08-29T07:44:06.365Z  ArrayBlockingQueue 总耗时:367

再增大各指标参数: Disruptor优势越来越明显

2019-08-29T07:50:59.911Z  线程数:64 单线程生产量: 65536 容量:1048576 数据总量:4194304
2019-08-29T07:51:28.075Z  EventProcessor wrapper
2019-08-29T07:51:28.075Z  disruptor start success!
2019-08-29T07:51:28.577Z  处理的sequence:4194303 count:4194304  Disruptor 总耗时:501

2019-08-29T07:51:11.549Z  ArrayBlockingQueue 生产耗时:11633
2019-08-29T07:51:11.575Z  处理count:4194304  ArrayBlockingQueue 消费耗时:11659
2019-08-29T07:51:11.575Z  ArrayBlockingQueue 总耗时:11659
2019-08-29T07:57:22.994Z  线程数:128 单线程生产量: 65536 容量:1048576 数据总量:8388608
2019-08-29T07:57:23.074Z  EventProcessor wrapper
2019-08-29T07:57:23.074Z  disruptor start success!
2019-08-29T07:57:24.036Z  处理的sequence:8388607 count:8388608  Disruptor 总耗时:961

2019-08-29T07:58:25.567Z  ArrayBlockingQueue 生产耗时:47941
2019-08-29T07:58:25.646Z  处理count:8388608  ArrayBlockingQueue 消费耗时:48020
2019-08-29T07:58:25.647Z  ArrayBlockingQueue 总耗时:48021

再大线程数, ArrayBlockingQueue 更耗时了,而Disruptor仍旧很快

2019-08-29T08:05:17.927Z  线程数:256 单线程生产量: 65536 容量:1048576 数据总量:16777216
2019-08-29T08:05:18.026Z  EventProcessor wrapper
2019-08-29T08:05:18.027Z  disruptor start success!
2019-08-29T08:05:20.060Z  处理的sequence:16777215 count:16777216  Disruptor 总耗时:2032

经测试发现: 

  1. 容量大小、消费者的消费速度  与耗时 成反比。
  2. Disruptor的性能在高并发、高数据规模(bufferSize 要大些)时表现更突出。
  3.  Disruptor与LinkedBlockingQueue(因读/写分开锁控制,比ArrayBlockingQueue性能好些)比对而言,当bufferSize大些的时候,也有优势。 

测试入口

package com.lmax.disruptor.noob;

import java.time.Instant;
import java.time.format.DateTimeFormatter;

/**
 * 担心影响, 分开执行测试
 * 
 * @author admin
 *
 */
public class CompareTest {
	public static int THREAD = 2 << 8; // 线程数量
	public static int PER = 2 << 10; // 单个线程生产数量
	public static int TOTAL_COUNT = THREAD * PER; // 数据总量
	public static int SIZE =32; // 最大容量

	public static void main(String[] args) {
		println("线程数:" + THREAD + " 单线程生产量: " + PER + " 容量:" + SIZE + " 数据总量:" + TOTAL_COUNT);
		 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.SIZE);

		AtomicBoolean endP = new AtomicBoolean(false);
		AtomicBoolean endC = new AtomicBoolean(false);
		long startTime = System.currentTimeMillis();
		AtomicLong count = new AtomicLong(0);
		for (int i = 0; i < CompareTest.THREAD; i++) {
			final int m = i;
			new Thread(() -> {
				for (int j = 0; j < CompareTest.PER; j++) {
					try {
						queue.put("i" + m + "j" + j); // 队列不够,等待生产
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					if (count.incrementAndGet() == CompareTest.TOTAL_COUNT) {
						CompareTest.println("ArrayBlockingQueue 生产耗时:" + (System.currentTimeMillis() - startTime));
						endP.set(true);
					}
				}
			}).start();
		}

		new Thread(() -> {
			AtomicLong consumerCount = new AtomicLong(0);
			while (true) {
				try {
					queue.take(); // 直到消费完所有信息
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if (consumerCount.incrementAndGet() == CompareTest.TOTAL_COUNT) {
					break;
				}
			}
			CompareTest.println("处理count:" + consumerCount.get() + "  ArrayBlockingQueue 消费耗时:"
					+ (System.currentTimeMillis() - startTime));
			endC.set(true);
		}).start();

		while (!(endC.get() && endP.get())) {}
		
       CompareTest.println("ArrayBlockingQueue 总耗时:" + (System.currentTimeMillis() - startTime));

	}
}

Disruptor 测试用例

    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() {
    
    		Disruptor<DataEvent> disruptor = new Disruptor<DataEvent>(new DataEventFactory(), CompareTest.SIZE,
    				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());
    
    		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();
    
    		for (int l = 0; l < CompareTest.THREAD; l++) {
    			new Thread(() -> {
    				for (int m = 0; m < CompareTest.PER; m++) {
    					producer.onData(start);
    					// translator.onData(start);
    				}
    			}).start();
    		}
    		/**
    		 * 关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;并不会自动关闭外部指定的executor,需要主动关闭
    		 */
    		// disruptor.shutdown();
            // 	CompareTest.println("disruptor shutdown success!");
    	}
    }
    

    事件

    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 {
    	private AtomicInteger count = new AtomicInteger(0);
    
    	@Override
    	public Object newInstance() {
    		CompareTest.println("dataEvent newInstance: " + count.incrementAndGet());
    		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/finally保证事件一定会被发布)。
     * 如果我们使用RingBuffer.next()获取一个事件槽,那么一定要发布对应的事件。如果不能发布事件,那么是不会消费的。
     * 
     * @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创建的
    		 */
    		if (count.incrementAndGet() == CompareTest.TOTAL_COUNT) {
    			CompareTest.println("处理的sequence:" + sequence + " count:" + count.get() + "  Disruptor 总耗时:"
    					+ (System.currentTimeMillis() - event.getStartTime()));
    		}
    	}
    
    }
    
    标签
    易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
    该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!