disruptor并发编程一:入门使用

disruptor并发编程一:入门使用

  • Martin Fowler写了一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金融交易平台,它能够以很低的延迟产生大量交易。这个系统是建立在JVM平台上,其核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中,使`用事件源驱动方式。业务逻辑处理器的核心是Disruptor。
  • Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。
  • Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。

一、引入disruptor框架

目前disruptor已经更新到了3.x版本,比之前的2.x版本性能更加的优秀,提供更多的API使用方式,直接下载jar包,addTopath就可使用,用如下maven依赖也可:

<!-- https://mvnrepository.com/artifact/com.lmax/disruptor -->
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.4.2</version>
</dependency>

二、示例

步骤:
第一:定义一个Event类,是一个常规javabean。
第二:继承disruptor工厂类,实现能产生Event实例的工厂,用于创建Event类实例对象。
第三:继承disruptor处理器,实现一个监听事件类,用于处理数据(Event类)
第四:应用层使用:实例化Disruptor实例,配置参数。然后我们对Disruptor实例绑定监听事件类,接受并处理数据。
第五:在Disruptor中,真正存储数据的核心叫做RingBuffer,我们通过Disruptor实例拿到它,然后把数据生产出来,把数据加入到RingBuffer的实例对象中,以便让第四步绑定的监听器处理数据。

代码:
(1) StringEvent.java 纯javabean

package com.ty.disruptor;

 public class StringEvent {	
	private Integer id;	
	private String value;	
	public Integer getId() {		
		return id;
	}	
	public void setId(Integer id) {		
		this.id = id;
	}	
	public String getValue() {		
		return value;
	}	
	public void setValue(String value) {		
		this.value = value;
	}	
	
	@Override
	public String toString() {		
		return "StringEvent [id=" + id + ", value=" + value + "]";
	}
}

(2) StringEventFactory.java 继承disruptor工厂类,实现能产生Event实例的工厂

package com.ty.disruptor;
import com.lmax.disruptor.EventFactory;

public class StringEventFactory implements EventFactory<StringEvent> {	
	@Override
	public StringEvent newInstance() {	
		return new StringEvent();
	}

}

(3)继承disruptor处理器,实现一个监听事件类,用于处理数据(Event类)

package com.ty.disruptor;
import com.lmax.disruptor.EventHandler;

public class StringEventHandler implements EventHandler<StringEvent> {	
	
	@Override
	public void onEvent(StringEvent stringEvent, long sequence, boolean bool) throws Exception {
		System.out.println("StringEventHandler(消费者):  "  + stringEvent +", sequence= "+sequence+",bool="+bool);    
	}

}

(4) StringEventProducer.java 一个事件源,用来触发disruptor事件

package com.ty.disruptor;
import java.nio.ByteBuffer;
import com.lmax.disruptor.RingBuffer;

public class StringEventProducer {     
	private final RingBuffer<StringEvent> ringBuffer;	
	public StringEventProducer(RingBuffer<StringEvent> ringBuffer) {	     
		this.ringBuffer = ringBuffer;
	}	 
	
	public void sendData(ByteBuffer byteBuffer) {  //ringBuffer就是用来存储数据的,具体可以看disruptor源码的数据结构,next就是获取下一个空事件索引
		long sequence = ringBuffer.next();	     //通过索引获取空事件 	
		try {			
			StringEvent stringEvent = ringBuffer.get(sequence);	//切换成读模式
			byteBuffer.flip();			//从byteBuffer中读取传过来的值
			byte[] dst = new byte[byteBuffer.limit()];
			byteBuffer.get(dst, 0, dst.length);	//为stringEvent赋值,填充数据
			stringEvent.setValue(new String(dst));
			stringEvent.setId((int) sequence);	//clear一下缓冲区
			byteBuffer.clear();
		} finally {			//发布事件,为确保安全,放入finally中,不会造成disruptor的混乱
			ringBuffer.publish(sequence);
		}
	} 
}

另外一种替代的写法,disruptor3推荐的,这样更灵活。
StringEventProducerWithTranslator.java

package com.ty.disruptor;
import java.nio.ByteBuffer;
import com.lmax.disruptor.EventTranslatorOneArg;
import com.lmax.disruptor.RingBuffer;

/*
 * Translator可以看做一个事件初始化器
 */ 
 public class StringEventProducerWithTranslator {	
 private final RingBuffer<StringEvent> ringBuffer;	
	//填充数据
	public static final EventTranslatorOneArg<StringEvent, ByteBuffer> TRANSLATOR = new EventTranslatorOneArg<StringEvent, ByteBuffer>() {		
		@Override
		public void translateTo(StringEvent stringEvent, long sequence, ByteBuffer byteBuffer) { // 从byteBuffer中读取传过来的值
			byteBuffer.flip();			
			byte[] dst = new byte[byteBuffer.limit()];
			byteBuffer.get(dst, 0, dst.length);
			byteBuffer.clear();  //为stringEvent赋值,填充数据
			stringEvent.setValue(new String(dst));
			stringEvent.setId((int) sequence);
		}
	};	

	public StringEventProducerWithTranslator( RingBuffer<StringEvent> ringBuffer) {		
		this.ringBuffer = ringBuffer;
	}	

	//发布事件 
   	public void sendData(ByteBuffer byteBuffer) {
		ringBuffer.publishEvent(TRANSLATOR, byteBuffer);
	}
}

发送数据与填充数据两个动作被Translator进行了分离。

(5) 应用层调用


public class AppMain {

	public static void main(String[] args) throws Exception {
		//创建一个线程池
		ExecutorService executorPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
		//创建Event工厂
		StringEventFactory factory = new StringEventFactory();
		/*
		 *	  创建Disruptor对象
		 *   eventFactory, 传入实现了EventFactory借口的工厂类
		 *   ringBufferSize, 用来存储数据的, 值为 2^n  
		 *   executor, 线程池 
		 *   producerType, 类型,可以是多个生产者,也可以是单个生产者 
		 *   waitStrategy, 使用什么策略,消费者如何等待生产者放入disruptor中 :
				     BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现
					 SleepingWaitStrategy 的性能表现跟BlockingWaitStrategy差不多,对CPU的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景
					 YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于CPU逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性
		 */
		Disruptor<StringEvent> disruptor = new Disruptor<>(factory, (int)Math.pow(2, 20), executorPool, ProducerType.SINGLE, new YieldingWaitStrategy());
		//关联处理器,也就是消费者,连接消费事件方法
		disruptor.handleEventsWith(new StringEventHandler());
		//启动
		disruptor.start();
		//获取RingBuffer,模拟生产者发布消息
		RingBuffer<StringEvent> ringBuffer = disruptor.getRingBuffer();
		
		StringEventProducerWithTranslator producer = new StringEventProducerWithTranslator(ringBuffer);
		//StringEventProducer producer = new StringEventProducer(ringBuffer);
		ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
		
		//闭锁控制线程同步
		CountDownLatch countDownLatch = new CountDownLatch(1);
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 0 ; i < 10 ; i ++) {
					//下面是进行触发事件并且发布
					byteBuffer.put(new String("生产者发布第"+ i +"条消息").getBytes());
					producer.sendData(byteBuffer);
					//模拟进行其他操作的耗时
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				countDownLatch.countDown();
			}
		},"Thread2").start();;
		//等待
		countDownLatch.await();
		disruptor.shutdown(); //关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;
		executorPool.shutdown(); //关闭使用的线程池;如果需要的话,必须手动关闭, disruptor 在 shutdown 时不会自动关闭;   
	}
}

运行结果

StringEventHandler(消费者):  StringEvent [id=0, value=生产者发布第0消息], sequence= 0,bool=true
StringEventHandler(消费者):  StringEvent [id=1, value=生产者发布第1消息], sequence= 1,bool=true
StringEventHandler(消费者):  StringEvent [id=2, value=生产者发布第2消息], sequence= 2,bool=true
StringEventHandler(消费者):  StringEvent [id=3, value=生产者发布第3消息], sequence= 3,bool=true
StringEventHandler(消费者):  StringEvent [id=4, value=生产者发布第4消息], sequence= 4,bool=true
StringEventHandler(消费者):  StringEvent [id=5, value=生产者发布第5消息], sequence= 5,bool=true
StringEventHandler(消费者):  StringEvent [id=6, value=生产者发布第6消息], sequence= 6,bool=true
StringEventHandler(消费者):  StringEvent [id=7, value=生产者发布第7消息], sequence= 7,bool=true
StringEventHandler(消费者):  StringEvent [id=8, value=生产者发布第8消息], sequence= 8,bool=true
StringEventHandler(消费者):  StringEvent [id=9, value=生产者发布第9消息], sequence= 9,bool=true

每隔一秒事件源sendData(),消费者就开始消费数据,这里实质上是一种观察者模式,内部状态改变会通知所有的消费者,有兴趣的可以去了解下观察者模式。

参考:

  1. https://www.imooc.com/article/68795
  2. http://ifeve.com/the-disruptor-lock-free-publishing/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值