Disruptor学习

从功能上来看,Disruptor 是实现了“队列”的功能,而且是一个有界队列。那么它的应用场景自然就是“生产者-消费者”模型的应用场合了。

可以拿 JDK 的 BlockingQueue 做一个简单对比,以便更好地认识 Disruptor 是什么。我们知道 BlockingQueue 是一个 FIFO 队列,生产者(Producer)往队列里发布(publish)一项事件(或称之为“消息”也可以)时,消费者(Consumer)能获得通知;如果没有事件时,消费者被堵塞,直到生产者发布了新的事件。这些都是 Disruptor 能做到的,与之不同的是,Disruptor 能做更多:

  1. 同一个“事件”可以有多个消费者,消费者之间既可以并行处理,也可以相互依赖形成处理的先后次序(形成一个依赖图);
  2. 预分配用于存储事件内容的内存空间;
  3. 针对极高的性能目标而实现的极度优化和无锁的设计;

以上虽然简单地描述了 Disruptor 是什么,但对于它"能做什么",还不是那么明白。简而言之,当你需要在两个独立的处理过程之间交换数据时,就可以使用 Disruptor 。当然使用队列也可以,只不过 Disruptor 的性能更好。

定义事件与事件工厂:

事件(Event)就是通过 Disruptor 进行交换的数据类型。

事件工厂(Event Factory)定义了如何实例化事件(Event),需要实现接口 com.lmax.disruptor.EventFactory<T>。Disruptor 通过EventFactory在 RingBuffer 中预创建 Event 的实例。一个 Event 实例实际上被用作一个“数据槽”,发布者发布前,先从RingBuffer 获得一个Event的实例,然后往Event 实例中填充数据,之后再发布到 RingBuffer 中,之后由Consumer 获得该 Event 实例并从中读取数据。

定义事件处理的具体实现:

通过实现接口 com.lmax.disruptor.EventHandler<T> 定义事件处理的具体实现,实际上就是消费者。EventHandlers是BatchEventProcessor要用到的, WorkHandler是WorkerPool要用到的。

等待策略:

       Disruptor 定义了 com.lmax.disruptor.WaitStrategy 接口用于抽象Consumer如何等待新事件,这是策略模式的应用。

       Disruptor 提供了多个WaitStrategy 的实现,每种策略都具有不同性能和优缺点,根据实际运行环境的 CPU 的硬件特点选择恰当的策略,并配合特定的 JVM 的配置参数,能够实现不同的性能提升。

       例如,BlockingWaitStrategy、SleepingWaitStrategy、YieldingWaitStrategy 等,其中,BlockingWaitStrategy 是最低效的策略,但其对CPU的消耗最小并且在各种不同部署环境中能提供更加一致的性能表现;SleepingWaitStrategy的性能表现跟BlockingWaitStrategy差不多,对CPU的消耗也类似,但其对生产者线程的影响最小,适合用于异步日志类似的场景;YieldingWaitStrategy 的性能是最好的,适合用于低延迟的系统。在要求极高性能且事件处理线数小于CPU逻辑核心数的场景中。

       • BlockingWaitStrategy: 对低延迟和吞吐率不像CPU占用那么重要

       • BusySpinWaitStrategy: CPU使用率高,低延迟

       • LiteBlockingWaitStrategy: BlockingWaitStrategy变种,实验性的

       • PhasedBackoffWaitStrategy: Spins, then yields, then waits,不过还是适合对低延迟和吞吐率不像CPU占用那么重要的情况

       • SleepingWaitStrategy: spin, then yield,然后sleep(LockSupport.parkNanos(1L))在性能和CPU占用率之间做了平衡。

       • TimeoutBlockingWaitStrategy: sleep一段时间。 低延迟。

       • YieldingWaitStrategy: 使用spin, Thread.yield()方式

发布事件:

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

       第一步:先从 RingBuffer 获取下一个可以写入的事件的序号;

       第二步:获取对应的事件对象,将数据写入事件对象;

       第三部:将事件提交到 RingBuffer;

       事件只有在提交之后才会通知 EventProcessor 进行处理;ringBuffer.publish 方法最好包含在 finally中以确保必须得到调用;因为如果某个请求的sequence未被提交,将会堵塞后续的发布操作或者其它的producer。

关闭 Disruptor:

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值