其他分享
首页 > 其他分享> > Disruptor初步认识

Disruptor初步认识

作者:互联网

Disruptor

文章参考:http://ifeve.com/ringbuffer/

背景介绍

Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能。

★需要特别指出的是,这里所说的队列是系统内部的内存队列,而不是Kafka这样的分布式队列。

技术介绍

Disruptor是基于无锁的并发“生产者 - 消费者模型”构建的。在保证逻辑正确的前提下,尽可能地提高队列在并发情况下的性能,Disruptor 采用了“两阶段写入”的方法。在写入数据之前,先加锁申请批量的空闲存储单元,之后往队列中写入数据的操作就不需要加锁了,写入的性能因此就提高了。Disruptor 对消费过程的改造,跟对生产过程的改造是类似的。它先加锁申请批量的可读取的存储单元,之后从队列中读取数据的操作也就不需要加锁了,读取的性能因此也就提高了(即在读和写之前均加锁,然后在真正的读和写过程中不加锁)。

对于生产者

对于生产者来说,它往队列中添加数据之前,先申请可用空闲存储单元,并且是批量地申请连续的 n 个(n≥1)存储单元。当申请到这组连续的存储单元之后,后续往队列中添加元素,就可以不用加锁了,因为这组存储单元是这个线程独享的。

对于消费者

对于消费者来说,处理的过程跟生产者是类似的。它先去申请一批连续可读的存储单元(这个申请的过程也是需要加锁的),当申请到这批存储单元之后,后续的读取操作就可以不用加锁了。

核心数据结构-RingBuffer

RingBuffer是一个环形数组,你可以把它当作在不同上下文(线程)间传递数据的buffer。

RingBuffer拥有一个序号,这个序号指向数组中下一个可用的元素。

随着你不停地填充这个buffer,这个序号会一直增长,直到绕过这个环(注意:图中的黑色数字代表的是序号值,不是数组的真正下标)。

若想找到数组中当前序号所指向的元素,可以通过mod操作获得。

序号 % arr.length = arrIndex
12 % 10 = 2
2代表->数组下标是2

事实上,上图中的Ringbuffer只有10个槽完全是个意外。如果槽的个数是2的N次方将更有利于基于二进制的机器运算。

2的N次方换成二进制就是1000,100,10,1这样的数字,
序号 & arr.length-1 = arrIndex,
比如一共有8槽,3&(8-1)=3,HashMap就是用这个方式来定位数组元素的,这种方式比取模的速度更快。

当另外一个服务通过nak (拒绝应答信号)告诉我们没有成功收到消息时,我们会从nak的那一个地方重新开始发送(因为RingBuffer不会删除已发送的数据,除非数据被覆盖了)。

收到10号nak,那就从10号重新开始发送:10、11、12。。。

优点

使用了数据,可以为数组预先分配好内存,使得数组对象一直存在,不会频繁的发生垃圾回收,此外,它也不会像链表那样,需要花时间在创建结点上,并且删除结点也是需要时间的,而RingBuffer没有删除操作,而是对原数据进行覆盖。

还未提到的点

RingBuffer的环重叠问题、如何对RingBuffer进行读写

标签:Disruptor,加锁,10,队列,初步,认识,RingBuffer,存储单元
来源: https://www.cnblogs.com/whyblogs/p/16242714.html