一 简介
Martin Fowler在自己网站上写了一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金融交易平台,它能够以很低的延迟产生大量交易。这个系统是建立在JVM平台上,其核心是一个业务逻辑处理器,它能够在一个线程里每秒处理6百万订单。业务逻辑处理器完全是运行在内存中,使用事件源驱动方式。业务逻辑处理器的核心是Disruptor。Disruptor它是一个开源的并发框架,并获得2011 Duke’s 程序框架创新奖,能够在无锁的情况下实现网络的Queue并发操作。Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。
第一次看到Martin Fowler,是在他的一篇微服务的博客上看到的,讲述了分布式微服务的思想,有兴趣的可以去百度下。
二 使用
1 目前我们使用disruptor已经更新到了3.x版本,比之前的2.x版本性能更加的优秀,提供更多的API使用方式,直接下载jar包,addTopath就可以使用了。
2 也可以以maven的方式来引入
<!-- https://mvnrepository.com/artifact/com.lmax/disruptor --> <dependency> <groupId>com.lmax</groupId> <artifactId>disruptor</artifactId> <version>3.4.2</version> </dependency>
3 示例
下面以一个例子来使用disruptor框架:
先了解一下步骤:
第一:建立一个Event类
第二:建立一个工厂Event类,用于创建Event类实例对象
第三:需要有一个监听事件类,用于处理数据(Event类)
第四:我们需要进行测试代码编写。实例化Disruptor实例,配置参数。然后我们对Disruptor实例绑定监听事件类,接受并处理数据。
第五:在Disruptor中,真正存储数据的核心叫做RingBuffer,我们通过Disruptor实例拿到它,然后把数据生产出来,把数据加入到RingBuffer的实例对象中即可
(1) StringEvent.java 用来接受数据的javabean对象
package com.lxj.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工厂方法创建我们自定义的对象,工厂方法。
package com.lxj.disruptor;import com.lmax.disruptor.EventFactory;/* * 实现Disruptor提供的工厂接口,工厂方法模式 */public class StringEventFactory implements EventFactory<StringEvent> { @Override public StringEvent newInstance() { //我们自定义的对象 return new StringEvent(); } }
(3) StringEventHandler.java 这个类就是一个处理器,也可以说是消费者
package com.lxj.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 这是一个事件源,用来触发事件。
package com.lxj.disruptor;import java.nio.ByteBuffer;import com.lmax.disruptor.RingBuffer;/* * 这是一个事件源,模拟的就是网络或者磁盘IO发送数据过来,触发事件 * ByteBuffer中携带着数据 */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); } } }
(5)跟第(4)步一样,也可以有另外一种替代的写法,disruptor3推荐的,这样更灵活。
StringEventProducerWithTranslator.java
package com.lxj.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); } }
发送数据跟填充数据进行了分离。
(6) StringEventMain.java 主函数进行测试:
package com.lxj.disruptor; import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; import com.lmax.disruptor.RingBuffer; import com.lmax.disruptor.YieldingWaitStrategy; import com.lmax.disruptor.dsl.Disruptor; import com.lmax.disruptor.dsl.ProducerType; /* * 测试主函数 */ public class StringEventMain { 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(); //关闭线程池 } }
运行结果:
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(),消费者就开始消费数据,这里实质上是一种观察者模式,内部状态改变会通知所有的消费者,有兴趣的可以去了解下观察者模式。
三 这是disruptor的一个helloWorld,下一篇博客会继续写关于disruptor。
共同学习,写下你的评论
评论加载中...
作者其他优质文章