Disruptor 简介
来源:互联网 发布:java获取jar包中文件 编辑:程序博客网 时间:2024/06/06 05:58
- 总体介绍
disruptor
是LMAX 开源的一个无锁循环队列,队列内部采用数组实现,通过Sequence
表示当前生产者可以提交元素的位置以及消费者消费的位置。Sequence
可以理解成一个AtomicLong
。生产者申请数据空间,并且将元素放入指定的空间中。消费者则从数组中取得相应的数据,进行消费.生产者角度来看,支持多生产者,单生产者.从消费者角度来看,支持单消费者,也支持多消费者,同时还支持具体依赖关系的不同消费者的消费. - 模块分析
Sequence
: 序列号,相当于AtomicLong
,有set,get,cas方法,其内部进行了缓存行填充,避免了伪共享问题。每一个生产者以及消费者内部都会维护一个Sequence
,表明当前正在消费的或者正在生产的索引值Sequencer
: Disruptor中一个核心的类,该类有两个实现(多生产者,单生产者),主要功能是保证正确的完成生产者与消费者之间的数据传递RingBuffer
:在Sequencer 的基础上,加入了数据元素的存储,会对数组进行预填充,大小必须为2的n次方。Disruptor
: 协调类,入口类,根据不同的参数,生成不同的生产者,消费者以及他们之间的协调关系。WaitStrategy
:等待策略,当生产者没有空间可以存放数据或者消息者没有数据可以消费时,以何种方式进行等等,有忙等待,sleep等待等共8种等待策略.Barrier
:消费者之间以及消费者与生产者之间协调的基础,其内部是其依赖的生产者及消息者的Sequence
,比如A消费者依赖于生产者,那么Barrier中的核心内容是生产者的Sequence,如果A依赖于B,B依赖于生产者,那么A的Barrier中保存的就是B的Sequence,如果A依赖于B及C。那么A中保存的就是B以及C的Sequenbce。通过Barrier可以获得接下来可以处理的数据位置EventFactory
: 生成Event的工厂,通过该工厂方法填充RingBuffer
EventProcessor
: 是一个Runnable, 其run方法就是消费者的消费过程EventHandler
处理,如果遇到异常,同时处理异常的过程.主要有两种实现,一是BatchEventProcessor
,批量获取需要处理的元素并处理, 二是WorkProcessor
,用于多个消费者情况.ExceptionHandler
: 异常处理器,如果处理运行中遇到的异常- 其它说明
gatingSequence
:是个Sequence的数组,保存着生产者直接依赖的最慢的消费者的Sequence。
代码示例
先上公共代码
// 事件处理器static class EventHandler implements com.lmax.disruptor.EventHandler<LongWrapper>{ String prefix; public EventHandler(String prefix){ this.prefix = prefix; } @Override public void onEvent(LongWrapper event, long sequence, boolean endOfBatch) throws Exception { System.out.println(prefix + event.getData() ); }}// EventFactorystatic class LongEventEventFactory implements com.lmax.disruptor.EventFactory<LongWrapper>{ @Override public LongWrapper newInstance() { return new LongWrapper(); }}// 具体的Eventstatic class LongWrapper{ private Long data; public Long getData() { return data; } public LongWrapper setData(Long data) { this.data = data; return this; }}// 具体的生产者static class Producer implements Runnable{ RingBuffer<LongWrapper> ringBuffer = null; public Producer(RingBuffer<LongWrapper> ringBuffer) { this.ringBuffer = ringBuffer; } AtomicLong atomicLong = new AtomicLong(0); @Override public void run() { while(true){ long next = ringBuffer.next(); try{ LongWrapper aLong = ringBuffer.get(next); aLong.setData(atomicLong.addAndGet(1)); TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } finally { ringBuffer.publish(next); } } }}
生产者角度:单生产者与多生产者在创建的时候传不同的
ProducerType
值即可.默认情况下是多生产者.单生产者与多生产者内部的实现是不同的. 从代码角度来看,基本上不致,这专门上代码. 下面的所有case都是以多生产者case来的.单消费者
public static void main(String[] args) throws InterruptedException { Executor executor = Executors.newCachedThreadPool(); final Disruptor<LongWrapper> longDisruptor = new Disruptor<LongWrapper>(new LongEventEventFactory(),512,executor); longDisruptor.handleEventsWith(new EventHandler("first handler")); longDisruptor.start(); new Thread(new Producer(longDisruptor.getRingBuffer())).start(); TimeUnit.HOURS.sleep(2);}// 结果如下first handler 1first handler 2first handler 3first handler 4first handler 5first handler 6.......
不具有依赖关系的消费者消费同一份数据
public static void main(String[] args) throws InterruptedException { Executor executor = Executors.newCachedThreadPool(); final Disruptor<LongWrapper> longDisruptor = new Disruptor<LongWrapper>(new LongEventEventFactory(),512,executor); longDisruptor.handleEventsWith(new EventHandler("first handler")); longDisruptor.start(); new Thread(new Producer(longDisruptor.getRingBuffer())).start(); TimeUnit.HOURS.sleep(2);}// 结果如下.可以看到 handler 1 与handler 2 不区分先后顺序, 因为生产者太慢.可以看到消费者每次基本上都同同消费1 ,同时消费2 first handler1second handler1first handler2second handler2first handler3second handler3first handler4second handler4second handler5first handler5.......
具有消费依赖关系的消费者消费同一份数据
// handler 3 与handler1 与hadnler 2 没有任何关系.handler2 必须在handler1消费以后再消费public static void main(String[] args) throws InterruptedException { Executor executor = Executors.newCachedThreadPool(); final Disruptor<LongWrapper> longDisruptor = new Disruptor<LongWrapper>(new LongEventEventFactory(),512,executor); EventHandler handler1 = new EventHandler("first handler"); EventHandler handler2 = new EventHandler("second handler"); EventHandler handler3 = new EventHandler("third handler"); longDisruptor.handleEventsWith(handler1); longDisruptor.after(handler1).handleEventsWith(handler2); longDisruptor.handleEventsWith(handler3); longDisruptor.start(); new Thread(new Producer(longDisruptor.getRingBuffer())).start(); TimeUnit.HOURS.sleep(2);}// 结果如下.可以看到handler 3的顺序可能在handler 1的前面.也可能handler 2的后面.也可能在两者中间. 而handler 2 一定在handler 1 的后面first handler1second handler1third handler1third handler2first handler2second handler2third handler3first handler3second handler3first handler4second handler4third handler4first handler5second handler5first handler6second handler6third handler5.......
同一个元素只能被多个消费者中的某一个消费
public static void main(String[] args) throws InterruptedException { Executor executor = Executors.newCachedThreadPool(); final Disruptor<LongWrapper> longDisruptor = new Disruptor<LongWrapper>(new LongEventEventFactory(),512,executor); longDisruptor.handleEventsWithWorkerPool( new WorkHandler<LongWrapper>() { @Override public void onEvent(LongWrapper event) throws Exception { System.out.println("first handler" + event.getData()); } }, new WorkHandler<LongWrapper>() { @Override public void onEvent(LongWrapper event) throws Exception { System.out.println("second handler" + event.getData()); } }, new WorkHandler<LongWrapper>() { @Override public void onEvent(LongWrapper event) throws Exception { System.out.println("third handler" + event.getData()); } }); longDisruptor.start(); new Thread(new Producer(longDisruptor.getRingBuffer())).start(); TimeUnit.HOURS.sleep(2);}// 结果如下,可以看到同一个元素只能被某一个handler处理second handler1third handler2first handler3second handler4third handler5first handler6second handler7third handler8first handler9second handler10third handler11first handler12second handler13third handler14first handler15......
总结及说明
生产者支持多生者与单生产者.默认为多生产者.对于消费者,支持单消费者,支持多消费者,支持多消费者之间的依赖处理,支持多消费者处理同一份数据以及某一个数据只能被多消费者中间的某一个消费者处理.
调用disruptor.start()
方法后,consumer开始消费开始.每一个EventHandler或者WorkHandler总终都被线程池中的一个线程执行. 所以假如你有三个handler在执行,线程池中一定要有三个线程,当不传线程池的时候,会采用默认的线程池,默认的线程池就是新起一个线程,对于此处来讲,完全可以采用默认的线程线.
- 其它说明
在前文中没有提到ExceptionHandler
,如果没有给Disruptor
设置异常处理器,那么假如消费者在处理任务的过程中.而默认的异常处理器会找印相应的日志后.再将异常抛出.从而导致消费者的线程死掉. RingBuffer被生产者填满,导致内存释放不掉.笔者在生产环境就遇到过由于消费者线程死掉导致不停的fucc gc的案例. 同时需要说明的是,为disruptor设置exceptionHandler 时,一定要先于调用其的handleEventsWith
,handleEventsWithWorkerPool
等方法. 否则无效. - 参考文献
- http://blog.csdn.net/zhxdick/article/category/6121943
- http://ifeve.com/disruptor/
- https://github.com/LMAX-Exchange/disruptor/wiki/Getting-Started
- https://github.com/LMAX-Exchange/disruptor/wiki/Introduction
- http://www.360doc.com/content/15/0330/20/11962419_459384128.shtml
1 0
- Disruptor简介
- Disruptor 简介
- Disruptor简介
- 转载:disruptor简介
- disruptor 简介《转载》
- Disruptor
- disruptor
- disruptor
- disruptor
- disruptor
- disruptor
- Disruptor
- Disruptor
- Disruptor
- Disruptor
- Disruptor
- Disruptor
- Disruptor
- 工作日记
- 腾讯后台开发面试
- 二、2、2Guava的新型集合
- 初学C语言
- markdown编辑器使用心得
- Disruptor 简介
- 直接插入排序
- BZOJ 1854 [Scoi2010]游戏——二分图匹配
- Git study notes
- 腾讯后台开发一面二面纪实
- 【前端-笔记】快乐的sublime编辑器(mac操作版)
- Android Activity四种启动模式
- 达内课程-JAVA八种基本类型
- Winform快捷键小结