disruptor应用

文章:




并发带来的问题:
1.cpu上下文,包括寄存器一二级缓存的失效,这个是由于线程阻塞切换,伪缓存引起。
2.高昂的线程切换

disruptor解决:
1.单线程处理,防止cpu上下文由于多线程切换而导致失效
2.cpu伪缓存问题
3.所有相应操作都在内存,提升速度
4.无锁cas解决生产者放入消息问题

现有应用:lmax,金融撮合交易,每秒是6百万tps.

知识点整理:

0.整体结构

其中SequenceBarrier用于消费者再获取相应消费seq时需要执行的逻辑,默认当当前消费者pos+1大于等于生产者全局seq时,会执行阻塞当前消费者。

Sequence就是序列,具体ringBuffer载体有个全局的写seq。相应消费者有自己的seq。
一个EventHandler处理器对应一个消费线程。

1.存储结构

RingBuffer,环形数组。

2.生产者如何写入
流程如下:
a.获取seq,将seq加1,这个位置对应event就是要写入的事件
b.写入数据到event,并提交

生产者如何防止并发写入:
通过cas,每个生产者可以申请写入一个区间seq。申请成功后再进行相应的提交数据到seq。

3.消费者如何消费
每个消费者保存自己的消费进度,可以批量消费

4.写入消息时超过ringBuffer如何处理
ConsumerTrackingProducerBarrier 用于保存所有消费者消费进度。
当当前要写入的seq等于当前有个消费者的消费指针时,则loop等待。

5.为何高效
a.单线程处理,防止cpu上下文由于多线程切换而导致失效
b.cpu伪缓存问题
c.所有相应操作都在内存,提升速度
d.环形数组,避免垃圾回收,链表有个问题就是创建结点与回收结点
e.无锁设计,生产者在写入时,先cas获取相应的seq再进行写入

6.应用场景
两大应用场景:
a.电商下单,对每个商品做disruptor,但前提是商品不能太多,还可以根据商品唯一标识先做hash然后再求模映射到相应的disruptor
多个请求放入到ringBuffer中,如果其中放入该请求发生阻塞,则会影响另一个请求的放入。
且对于这个业务逻辑,每个请求只需要处理一次,

b.金融撮合交易
按对外产品维度,每个产品做一个disruptor。或者可以设定一个固定slot数量。然后根据产品id做hash,再对slot取模。

具体思路是:

涉及模块:
Disruptor生产者 -> Disruptor消费处理器 ->业务处理模块

进入的请求线程,给它一个唯一标识,然后封装成任务交给disruptor生产者组件。
disruptor生产者负责写入任务到ringBuffer,然后返回一个DefaultFuture。
相应事件处理器handler负责将请求交给相应具体业务模块。
业务模块处理完成后,将结果交给事件处理器,事件处理器负责将结果设置到DefaultFuture。

请求线程调用DefaultFuture的get方法获取结果,超时或拿到结果后返回给客户端。

7.存在的坑
多生产者的情况下,每个生产者去拿序号,拿到序号:x的生产者,和拿到x+1的生产者。
这种情况下,拿到x+1序号的生产者必须等待x的生产者提交,也就是说不同生产者会相互阻塞。

还有种情况会阻塞,就是当前获取要写入的seq等于最慢的一个消费者的指针,这里需要park。

8.调优



猜你喜欢

转载自blog.csdn.net/zhaozhenzuo/article/details/77430243