Disruptor(二)基础开发

1、获取Disruptor

Maven仓库

https://search.maven.org/search?q=g:com.lmax%20AND%20a:disruptor

源码

https://github.com/LMAX-Exchange/disruptor

2、一个简单的事件生产者和消费者示例

首先创建一个事件数据类型:

 创建一个事件工厂,使Disruptor可以用来生成RingBuffer的预分配条目:

创建一个事件消费者:

 在本例中,我们假设这个事件数据是来自于某种I/O设备,源数据封装在ByteBuffer中。

从3.0版本开始,Disruptor支持Lambda风格的API,帮助开发人员可以把复杂性封装在RingBuffer中,建议的发布消息的方法是通过结合使用翻译器(Translator)来发布数据(Event Publisher/Event Translator)。

该方法的好处是可以把数据转换的代码单独剥离出来,便于进行单元测试。Disruptor提供了多种不同的转换器接口(EventTranslator、EventTranslatorOneArg、EventTranslatorTwoArg等)。

 也可以用非Lambda风格的传统代码来编写生产者:

从上面的代码片段可以看出,在底层代码逻辑中,发布一个事件需要两个步骤,第一个步骤是从RingBuffer中获取一个可用的数据槽(调用RingBuffer.next()),同时需要把发布操作封装在一个try/finally代码块中,每当获取了一个数据槽,那么必须要发布这个sequence。如果不遵守这个规则,那么会导致Disruptor中的状态损坏,特别是在多个生产者的场景中,会导致消费者停止消费,只有重启系统才能重置状态。因此,这也是推荐使用EventTranslator API的原因。

最后一个步骤是把上述的各个部分组装起来,可以用较复杂的纯手工组装,也可以使用简易的基于DSL方式的组装,虽然一些复杂的配置选项不能用DSL,但是DSL已经可适用于大多数场合。

 

使用Java8函数风格的API:

在代码中使用了匿名函数,节省了两个类handler、producer。

 不使用匿名函数,可以使用方法引用:

3、基本的调优选项

提高性能的调优选项,主要有两个方面:(1)单个或者多个生产者;(2)等待事件到达的策略。

3.1 Single vs. Multiple Producers

在并行系统中,提高性能的方法是遵从单写原则(Single Writer Principle),单写原则可以避免临界资源的锁竞争。

性能差异可以通过测试程序检测:

https://github.com/LMAX-Exchange/disruptor/blob/master/src/perftest/java/com/lmax/disruptor/sequenced/OneToOneSequencedThroughputTest.java

 从测试结果看,单写模式比多写模式性能高出2.8倍左右。

3.2 等待事件到达的策略

  • BlockingWaitStrategy

Disruptor的默认等待策略是BlockingWaitStrategy,该策略的内部使用锁和条件变量来处理线程的唤醒。在所有的等待策略中BlockingWaitStrategy是最慢的,但是对CPU的使用又是最保守的,在大多数部署场景中,该策略的行为表现是最恒定的。

  • SleepingWaitStrategy

SleepingWaitStrategy也是试图保守使用CPU,内部使用了一个简单的忙等待循环(busy wait loop),并在循环中调用LockSupport.parkNanos(1),在一个典型的Linux系统中会暂停线程大约60μs。

这个策略可以避免使用计数器、和对条件变量发送信号方面的消耗,但是在生产者和消费者线程之间移动事件的平均延迟将会更高。

SleepingWaitStrategy适用于对低延时不苛求,但是对生产者线程要求低影响的场景中,例如异步写日志就是一个适用的场景。

注意事项:On 32 bit Linux systems it appears that LockSupport.parkNanos() is quite expensive, therefore using the SleepingWaitStrategy is not recommended.

  • YieldingWaitStrategy

YieldingWaitStrategy适用于低延迟要求的系统中,该策略可以基于改进延迟的目标来选择刻录CPU周期,策略内部使用忙自旋(busy spin)来等待sequence增加到一个合适的值,在循环中调用Thread.yield()来允许其它排队线程去执行。

该策略适用于要求非常高的性能,并且事件处理线程小于CPU逻辑核数的场景,例如开启了超线程的部署环境。

  • BusySpinWaitStrategy

BusySpinWaitStrategy具有最高的性能,也就是最低的数据推送延迟,但是对部署环境的限制条件最高。只有在事件处理程序线程数量小于机器的物理核心数量时,才应使用此等待策略,例如,部署环境应禁用超线程。

​​​​​​​3.3 清理RingBuffer中的数据对象

当通过Disruptor传递数据,我们希望在数据处理完成之后,对RingBuffer中已处理数据进行及时清理。如果是单个事件处理器,那么在就在该事件处理器中执行清理。如果存在事件处理器链,那么应该在处理器链末端的处理器中执行清理。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kingspider-sh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值