每天一例多线程[day22]-----Disruptor并发框架简介HelloWorld

Disruptor是一个开源的并发框架,并获得2011年Duke's程序框架创新奖,能够在无锁的情况下实现网络的Queue队列并发操作。Disruptor是一个高性能的异步处理框架,或者可以认为是最快的消息框架(轻量的JMS),也可以认为是一个观察者模式的实现,或者事件监听模式的实现。也可以理解为一个高效的“生产者-消费者”模型。性能远远高于传统的BlockingQueue。

 

Disruptor的Ringbuffer模型:

分析:disruptor内部维护的Ringbuffer其实结构是一个首尾相接的环形,用作在多线程之间传递数据的buffer,ringbuffer有一个序号sequence,这个序号sequence指向数组中下一个可用的元素。随着生产者不停填充这个ringbuffer,这个序号sequence可能会一直增长,直到绕过这个环。如上图,生产者向一个容量为8的ringbuffer环中填充了8个数据从1到8,当sequence为0的元素1被消费者消费掉后,生产者就将下一个元素放置到原来存放1的sequence为0的位置上,但是此时的sequence不是0,而是接着增长到了9,这是因为ringbuffer维护的是一个AutomicLong的数值来表示sequence。

只要知道了sequence序号,就可以通过取模的方式得到元素在ringbuffer环中的位置:

                                       sequence mod array length = arrayIndex

以上边图中的ringbuffer为例,如果sequence=8,那么8%8=0,这样消费者就可以通过下标0拿到ringbuffer环数组的元素。

注意:上图中ringbuffer只有8个槽只是个例子,如果槽的个数是2的N次方,则会更有利于二进制计算机计算。

 

理解RingBuffer:

1 没有尾指针:Disruptor的ringbuffer没有尾指针,只维护了一个指向下一个可用位置的序号sequence,这种实现是设计者经过深思熟虑的,设计之初就是为了提供可靠的消息传递。

2 不需要删除元素:RingBuffer和常用队列区别是,我们不能删除RingBuffer中的数据,也就是说这些数据一直存放在RingBuffer中,直到新的数据覆盖它们,这也是之所以不需要尾指针的原因,因为根本不需要控制数据的覆盖。

3 RingBuffer是数组,所以比链表查询速度快,而且有一个可以预测的访问模式。

数组是对CPU友好的,在硬件层面,数组的元素是会被预加载的,因此在RingBuffer中,cpu不需要时不时的加载数据中下一个元素。

 

Disruptor的术语说明

WaitStrategy:决定消费者如何等待生产者将Event放入Disruptor。

Event:从生产者到消费者所处理的数据单元。Disruptor中没有代码定义Event,因此它完全是由用户来定义的。

EventProcessor:主要事件循环,处理Disruptor中的Event,并且拥有消费者的sequence。它有一个实现类是BatchEventProcessor,包含了Event loop有效的实现,并且将回调到一个EventHandler的接口实现对象。

EventHandler:由用户实现并且代表了Disruptor中的一个消费者的接口。

Producer:事件生产者,由用户实现,它调用Ringbuffer来插入事件Event,在Disruptor没有相应实现代码,由用户实现。

WorkProcessor:确保每一个sequence只被一个Processor消费,在同一个workPool中处理多个workProcessor不会消费同样的sequence。

WorkerPool:一个workProcessor池,其中workProcessor将消费Sequence,所以任务可以在实现WorkHandler接口的worker之间移交。

LifeCycleAware:由BatchEventProcessor启动和停止时实现这个接口用于接收通知。

 

HelloWorld代码实现:

LongEvent事件对象

 
  1. //http://ifeve.com/disruptor-getting-started/

  2.  
  3. /**

  4. * 生产者要生产的【event对象】,主动传递给disruptor中的RingBuffer

  5. * @author jeffSheng

  6. *

  7. */

  8. public class LongEvent {

  9.  
  10. private long value;

  11.  
  12. public long getValue() {

  13. return value;

  14. }

  15.  
  16. public void setValue(long value) {

  17. this.value = value;

  18. }

  19. }

LongEventFactory事件工厂

 
  1. import com.lmax.disruptor.EventFactory;

  2. /**

  3. * 实现disruptor的【事件工厂】EventFactory,让disruptor批量产生longEvent

  4. * @author jeffSheng

  5. *

  6. */

  7. public class LongEventFactory implements EventFactory {

  8.  
  9. @Override

  10. public Object newInstance() {

  11. return new LongEvent();

  12. }

  13. }

LongEventHandler事件处理器( 消费者)

 
  1. /**

  2. * 定义disruptor的【消费者】

  3. * 我们还需要一个事件消费者实现disruptor的EventHandler,也就是一个事件处理器。

  4. * 这个事件处理器简单地把事件中存储的数据打印到终端:

  5. * @author jeffSheng

  6. *

  7. */

  8. public class LongEventHandler implements EventHandler<LongEvent> {

  9.  
  10. @Override

  11. public void onEvent(LongEvent longEvent, long l, boolean b) throws Exception {

  12. System.out.println(longEvent.getValue());

  13. }

  14.  
  15. }

 

主函数:

 
  1. import java.nio.ByteBuffer;

  2. import java.util.concurrent.ExecutorService;

  3. import java.util.concurrent.Executors;

  4.  
  5. import com.lmax.disruptor.RingBuffer;

  6. import com.lmax.disruptor.YieldingWaitStrategy;

  7. import com.lmax.disruptor.dsl.Disruptor;

  8. import com.lmax.disruptor.dsl.ProducerType;

  9.  
  10. public class LongEventMain {

  11.  
  12. public static void main(String[] args) throws Exception {

  13. //创建缓冲线程池

  14. ExecutorService executor = Executors.newCachedThreadPool();

  15. //创建LongEvent事件工厂

  16. LongEventFactory factory = new LongEventFactory();

  17. //创建bufferSize缓冲区 ,也就是RingBuffer大小,要求必须是2的N次方

  18. int ringBufferSize = 1024 * 1024;

  19.  
  20.  
  21. /**

  22. * 创建disruptor实例,并传入泛型LongEvent事件类型(数据类型)

  23. * 构造参数:

  24. * Disruptor(

  25. * factory,--事件工厂,用于创建LongEvent,也就是实际最终被消费的数据

  26. * ringBufferSize,--缓冲区大小

  27. * executor,--线程池,作用是使用线程池进行内部数据接收处理调度

  28. * producerType,--两种形式:SINGLE(生产者只有一个)和MULTI(生产者有多个)

  29. * waitStrategy--决定一个消费者将如何等待生产者将EVENT放入disruptor

  30. * )

  31. * //BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小,并且在各种不同部署环境中能提供更加一致的性能表现

  32. WaitStrategy BLOCKING_WAIT = new BlockingWaitStrategy();

  33. //SleepingWaitStrategy 的性能表现跟BlockingWaitStrategy差不多,对CPU的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景

  34. WaitStrategy SLEEPING_WAIT = new SleepingWaitStrategy();

  35. //YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于CPU逻辑核心数的场景中,推荐使用此策略;例如,CPU开启超线程的特性

  36. WaitStrategy YIELDING_WAIT = new YieldingWaitStrategy();

  37. */

  38. Disruptor<LongEvent> disruptor =

  39. new Disruptor<LongEvent>(factory, ringBufferSize, executor, ProducerType.SINGLE, new YieldingWaitStrategy());

  40.  
  41. /**

  42. * 连接消费事件方法,监听ringbuffer环形队列容器中的事件,有则取出消费,无则阻塞,所以disruptor相当于一个特殊的有界阻塞队列

  43. * LongEventHandler 理解为数据消费者

  44. */

  45. disruptor.handleEventsWith(new LongEventHandler());

  46. // 启动disruptor

  47. disruptor.start();

  48.  
  49. //Disruptor 的事件发布过程是一个两阶段提交的过程:

  50. /**

  51. * 发布事件

  52. * 使用该方法获得具体存放数据的容器ringBuffer(环形结构)

  53. */

  54. RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();

  55. //定义事件生产者

  56. LongEventProducer producer = new LongEventProducer(ringBuffer);

  57. // LongEventProducerWithTranslator producer = new LongEventProducerWithTranslator(ringBuffer);

  58. //定义一个含有8个空间的字节缓冲

  59. ByteBuffer byteBuffer = ByteBuffer.allocate(8);

  60. for(long a = 0; a < 100; a++){

  61. byteBuffer.putLong(0, a);//每次覆盖byteBuffer下标为0的位置

  62. //producer将数据byteBuffer存入ringbuffer事件槽

  63. producer.onData(byteBuffer);

  64. //Thread.sleep(1000);

  65. }

  66.  
  67.  
  68. disruptor.shutdown();//关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;

  69. executor.shutdown();//关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭, disruptor 在 shutdown 时不会自动关闭;

  70.  
  71.  
  72.  
  73.  
  74.  
  75.  
  76.  
  77. }

  78. }

主函数代码分析

实现HelloWorld的步骤:

第一,建立Event类

第二,建立一个Event工厂类,用户实例化Event对象。

第三,需要一个事件监听器,用于处理Event类

以上三步:

 
  1. //创建缓冲线程池

  2. ExecutorService executor = Executors.newCachedThreadPool();

  3. //创建LongEvent事件工厂

  4. LongEventFactory factory = new LongEventFactory();

  5. //创建bufferSize缓冲区 ,也就是RingBuffer大小,要求必须是2的N次方

  6. int ringBufferSize = 1024 * 1024;

第四,编写主函数,实例化Disruptor实例,传入构造参数,然后disruptor绑定监听事件类,接收并处理数据。


 创建disruptor实例,并传入泛型LongEvent事件类型(数据类型)
 构造参数:
new  Disruptor(
 factory,--事件工厂,用于创建LongEvent,也就是实际最终被消费的数据
 ringBufferSize,--缓冲区大小
 executor,--线程池,作用是使用线程池进行内部数据接收处理调度
 producerType,--两种形式:SINGLE(生产者只有一个)和MULTI(生产者有多个)
 waitStrategy--决定一个消费者将如何等待生产者将EVENT放入disruptor
  )

 
  1. Disruptor<LongEvent> disruptor =

  2. new Disruptor<LongEvent>(factory, ringBufferSize, executor, ProducerType.SINGLE, new YieldingWaitStrategy());

  3.  
  4. /**

  5. * 连接消费事件方法,监听ringbuffer环形队列容器中的事件,有则取出消费,无则阻塞,所以disruptor相当于一个特殊的有界阻塞队列

  6. * LongEventHandler 理解为数据消费者

  7. */

  8. disruptor.handleEventsWith(new LongEventHandler());

  9. // 启动disruptor

  10. disruptor.start();

第五,在disruptor中,真正存储数据的核心是Ringbuffer,我们通过disruptor实例拿到了它,然后把数据生产了出来,把数据加入到Ringbuffer中即可。

 
  1. //定义事件生产者

  2. LongEventProducer producer = new LongEventProducer(ringBuffer);

 
  1. //定义一个含有8个空间的字节缓冲

  2. ByteBuffer byteBuffer = ByteBuffer.allocate(8);

  3. for(long a = 0; a < 100; a++){

  4. byteBuffer.putLong(0, a);//每次覆盖byteBuffer下标为0的位置

  5. //producer将数据byteBuffer存入ringbuffer事件槽

  6. producer.onData(byteBuffer);

  7. //Thread.sleep(1000);

  8. }

  9. disruptor.shutdown();//关闭 disruptor,方法会堵塞,直至所有的事件都得到处理;

  10. executor.shutdown();//关闭 disruptor 使用的线程池;如果需要的话,必须手动关闭, disruptor 在 shutdown 时不会自动关闭;

生产者定义方式一:

 
  1. import java.nio.ByteBuffer;

  2. import com.lmax.disruptor.RingBuffer;

  3.  
  4. public class LongEventProducer {

  5.  
  6. private final RingBuffer<LongEvent> ringBuffer;

  7.  
  8. public LongEventProducer(RingBuffer<LongEvent> ringBuffer){

  9. this.ringBuffer = ringBuffer;

  10. }

  11.  
  12. /**

  13. * onData用来发布事件,每调用一次就发布一次事件, 它的参数bb会通过事件传递给消费者

  14. * 将bb存入ringBuffer环形事件容器

  15. * 生产数据需要遵循的四个步骤

  16. */

  17. public void onData(ByteBuffer bb){

  18. //1.可以把ringBuffer看做一个环形事件队列,那么next就是得到下面一个空的事件槽索引

  19. long sequence = ringBuffer.next();

  20. try {

  21. //2.用上面的索引取出一个空的事件用于填充(获取该序号对应的事件对象)

  22. LongEvent event = ringBuffer.get(sequence);

  23. //3.获取要通过事件传递的业务数据

  24. event.setValue(bb.getLong(0));

  25. } finally {

  26. //4.发布事件

  27. /**

  28. * 注意,最后的 ringBuffer.publish 方法必须包含在 finally 中以确保必须得到调用;

  29. * 如果某个请求的 sequence 未被提交,将会堵塞后续的发布操作或者其它的 producer。

  30. * 只有发布后才能被监听的消费者消费掉

  31. */

  32. ringBuffer.publish(sequence);

  33. }

  34. }

  35. }

 

生产者定义方式二:

 
  1. import java.nio.ByteBuffer;

  2. import com.lmax.disruptor.EventTranslatorOneArg;

  3. import com.lmax.disruptor.RingBuffer;

  4.  
  5. /**

  6. * Disruptor 3.0提供了lambda式的API。这样可以把一些复杂的操作放在Ring Buffer,

  7. * 所以在Disruptor3.0以后的版本最好使用Event Publisher或者Event Translator来发布事件

  8. */

  9. public class LongEventProducerWithTranslator {

  10.  
  11. //一个translator可以看做一个事件初始化器,publicEvent方法会调用它

  12. //填充Event

  13. private static final EventTranslatorOneArg<LongEvent, ByteBuffer> TRANSLATOR =

  14. new EventTranslatorOneArg<LongEvent, ByteBuffer>() {

  15. @Override

  16. public void translateTo(LongEvent event, long sequeue, ByteBuffer buffer) {

  17. event.setValue(buffer.getLong(0));

  18. }

  19. };

  20.  
  21. private final RingBuffer<LongEvent> ringBuffer;

  22.  
  23. public LongEventProducerWithTranslator(RingBuffer<LongEvent> ringBuffer) {

  24. this.ringBuffer = ringBuffer;

  25. }

  26.  
  27. public void onData(ByteBuffer buffer){

  28. ringBuffer.publishEvent(TRANSLATOR, buffer);

  29. }

  30. }

 

结果打印:控制台从0打印到99.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值