Disruptor

Disruptor是一个高性能的内存消息队列,基于环形数组,比传统阻塞队列如ArrayBlockingQueue效率更高。它采用无锁化设计,通过CAS操作避免线程竞争,减少内存GC,利用先行填充技术消除伪共享,优化缓存效率。环形数组通过特殊的方法控制元素位置,确保不会因序号递增而导致溢出。Disruptor适用于复杂业务场景,如发布订阅和点对点模式。文章还介绍了Disruptor的基本使用、类图以及与JDK阻塞队列的对比示例。
摘要由CSDN通过智能技术生成

高性能内存队列-Disruptor

Disruptor是基于环形数组的内存消息队列,相比JDK提供的阻塞队列

  • 效率更高
  • 应用场景更多,除基本的发布订阅/点对点模式外,还可实现消费者间依赖,实现更复杂业务

在这里插入图片描述

一、为什么高效

  • 无锁化,普通阻塞队列ArrayBlockingQueue(为了防止生产者速度过快导致内存溢出,只能选择有界队列,同时为提高读取效率,JDK内只有它适合做队列),使用的是重量级锁Lock,生产者消费者put/take间是互斥关系,头尾指针竞争激烈,竞争将导致线程经常挂起,效率很低。Disruptor生产者消费者之间不互斥,各自维护各自的序列号,并且生产/消费者之间使用CAS乐观锁,不存在线程切换的开销,在持有锁时间较短的场景下效率很高(单生产者/消费者模型连CAS都不用)

  • 存储空间方案优化,普通阻塞队列take后元素主动置null,引发GC。Disruptor为循环覆盖,不存在GC

  • 利用先行填充消除伪共享

在这里插入图片描述

CPU和主内存之间是多级缓存,缓存越靠近主内存越慢,缓存最小存储单元是缓存行,一般是64字节,CPU读取变量时会加载它相邻的变量到同一个缓存行内,这也是为什么数组读取效率高的原因之一,也是为啥Disruptor要使用数组结构

但是这会存在一个问题,缓存行内任何一个变量改变,加载同一缓存行数据的其他线程内缓存都会失效,要重新加载数据到缓存

Disruptor的解决办法就是预先填充,如RingBuffer填充7个long类型数据,为保证cursor指针在缓存行内只加载一个有用数据,其他7个没人修改所以不会造成缓存失效

二、环形数组是怎么构造出来的
首先明确,所谓环形数组并不是真的环形结构,单纯是个普通数组,只不过通过get方法来控制获取的数据位,看起来就像是环形一样

如何确定元素位置

protected final E elementAt(long sequence){
   
    return (E) UNSAFE.getObject(entries, REF_ARRAY_BASE + ((sequence & indexMask) << REF_ELEMENT_SHIFT));
}
REF_ARRAY_BASE是基地址 UNSAFE.arrayBaseOffset(Object[].class)

(sequence & indexMask)可以确定数组下标

REF_ELEMENT_SHIFT为数组每个元素的存储空间大小 UNSAFE.arrayIndexScale(Object[].class)

((sequence & indexMask) << REF_ELEMENT_SHIFT)左移目的是往前跳N个格,每个格大小是REF_ELEMENT_SHIFT,结合起来构成了偏移量

基地址 + 偏移量,就能找到元素

三、序号是一直递增的,会不会爆?
不会,序号是long型,100万QPS的处理速度,也需要30万年才能用完 9223372036854775807 / 31536000 * 1000000 = 292471年

类图

在这里插入图片描述

基本使用

配置/启动 Disruptor

@PostConstruct
private void start() {
    Disruptor disruptor = new Disruptor<>(new PushMsgEventFactory(), bufferSize, Executors.newCachedThreadPool());
    WorkHandler[] workers = {new PushMsgHander()};
    // 多消费者模式
    disruptor.handleEventsWithWorkerPool(workers);
    // 开启队列
    disruptor.start();
}

// 事件工厂,环形数组初始化时会用该工厂填充整个桶(即事先填好空对象)

public class PushMsgEventFactory implements EventFactory<PushMsgEvent> {
   
    @Override
    public PushMsgEvent newInstance() {
   
        return new PushMsgEvent();
    }
}

// 生产者

private void pushMsg(PushMsgData pushMsgData) {
   
    // 获取环形队列RingBuffer(disruptor实例自己想办法注入)
    RingBuffer<PushMsgEvent> ringBuffer = disruptor.getRingBuffer();
    // 获取下一个序列号
    long sequence = ringBuffer.next()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值