一文说透Disruptor的工作机制以及核心源码解读 赋踩坑注意事项!

有时候在想我可以做些什么,一件有价值的事,一件让自己开心,也能让别人夸赞的事,也许是个系统,也许是个业务,也许小而美,也许平凡中伟大,真是一个好问题啊~ 也许首先要做的是写一篇博客,记录一下生活,依然-五一快乐

LMAX开发的高性能队列,传说可以达到单机每秒600万交易~YYDS是吗

Disruptor 主要解决了 JDK 内置线程安全队列的性能和内存安全问题;其在无锁的情况下还能保证队列有界线程安全

是基于事件异步驱动模型实现的

  1. 采用RingBuffer数据结构
  2. 支持高并发,低延时,高吞吐量的高性能工作队列;
  3. Disruptor是单机的本地内存队列,类似ArrayBlockingQueue

特点

  • 高性能
  • 低延迟
  • 无锁设计
  • 多生产者/消费者模型
  • 单机-纯内存
  • 可配置性强:提供多种事件处理策略;可通过配置不同的事件处理器来实现不同的处理逻辑
  • 时间重用
  • 事件分发机制:单播、广播
  • 生命周期管理
  • 异步处理

为什么比ArrayBlockingQueue快?

  1. 无锁设计:CAS
  2. 批量处理:一次获取多个事件处理
  3. 内存预分配:消除GC停顿
  4. 缓存优化:消除伪共享

使用场景

  1. 可用于撮合引擎、支付系统交易匹配
  2. 股票行情处理(每秒百万级报价)
  3. 游戏服务器战斗结算
  4. log4j2基于Disruptor实现异步日志处理
  5. SOFATracer:蚂蚁金服开源的分布式应用链路追踪工具使用Disruptor
  6. Storm:开源的分布式实时计算系统,基于Disruptor实现工作进程内发生的消息传递
  7. HBase:分布式列存储数据库系统,基于Disruptor提高写并发性能
  8. Canal+Dis实现高效数据同步

Disruptor核心

在这里插入图片描述

可以看到有几个概念,我们一一介绍下

  • Producer

发送者,没啥好说的

  • RingBuffer

在这里插入图片描述

它是环形数组结构,首尾相连,用作不同上下文间传递数据的buffer;

数组结构;结合SequenceBarrier机制,实现线程与线程之间高效的数据交互,数组元素不会被回收,避免频繁GC,同时数组对处理器的缓存机制更友好

无锁设计,CAS保证线程安全

RingBuffer拥有一个序号,这个序号指向数组中下一个可用元素,不论存取都是用CAS操作

特点:它只有一个指针,创建时大小就被固定了,初始化时维护一个object[] entries数组元素

生产者向RingBuffer写入消息时RingBuffer不是直接向数组元素entries指向event对象,而是先获取event对象,更改event对象中的detail属性

消费者也是从RingBuffer读取event,读取对象中的detail属性

由此可见,在生产和消费过程中 ,RingBuffer中的数组元素entries没有发生任何变化,没有产生临时对象,数组中的元素一直存活,直到RingBuffer消亡,降低GC,提高性能

优势
  1. 简化多线程同步的复杂度,像摩天轮转圈一样的数据容器,座位数量固定,但乘客不断上下,循环往复,两个指针(头指针、尾指针)像追逐游戏般移动;当指针达到数组末尾时,自动回到起点;指针的魔法移动:不用if判断,改用取模运算实现循环 next_pos = (current_pos + 1) % buffer_size

  2. 环形缓冲区=环形队列,固定尺寸,无需重复申请内存,首尾相连,可以做到覆盖旧数据-对象复用,减少GC压力,适合缓存数据流

  3. 预分配连续内存

  4. 消除了伪共享,如图,用的填充的方式

在这里插入图片描述

注意
  1. 在缓冲区满的时候写数据,有两种策略可以使用:第一覆盖掉老数据;第二抛出异常
  2. 读数据时,一定要读出缓冲区中最老的数据(如果被覆盖了,那最老的一定是下一位)
  3. 环中有开始位置、结束位置,读索引=最早放入的数据;写索引=下一个要写入的位置
  4. 环形缓冲区大小必须是2的幂
  5. 避免在EventHandler中阻塞操作(会拖慢整个处理链)
  6. 监控序列差值 getCursor()
  • sequence

    序号管理器,顺序增长,每个Consumer都维护一个Sequence,通过它标识和定位RingBuffer中的每一个事件,可以跟踪Consumer事件处理进度,它有AtomicLong的大多数功能特性,而且它消除了CPU伪共享的问题(通过填充的方式)

    里面是个long value,简单理解是高并发下优化的long类型

在这里插入图片描述

  • sequencer

Disruptor的核心,生产者与缓存RingBuffer之间的桥梁,

  • 有当前写的进度Sequence cursor
  • 有所有消费者进度的数组Sequence[] gatingSequences
这几个变量在AbstractSequencer抽象类中,都是重要参数
		protected final int bufferSize;
    protected final WaitStrategy waitStrategy;
    protected final Sequence cursor = new Sequence(Sequencer.INITIAL_CURSOR_VALUE);
    protected volatile Sequence[] gatingSequences = new Sequence[0];

接口;它有两个实现类:SingleProducerSequencer(单生产者实现)、MultiProducerSequencer(多生产者实现),它主要作用是实现生产者和消费者之间快速、正确传递数据的并发算法

生产者发布event的时候首先需要预定一个 sequence,Sequencer 就是计算和发布 sequence 的,下面分别介绍

SingleProducerSequencer

单生产者用到,发布过程

  • 通过 Sequencer.next(n) 来预定下面 n 个可以写入的位置序号,后面我们可以解析下源码
  • 根据序号获取事件,然后修改事件数据,然后发布 event
MultiProducerSequencer

多生产者场景使用,后面可以看下源码

  • sequenceBarrier

协调屏障,用来跟踪发布者的游标和事件处理者的序列号,保证事件的有序性

是消费者与RingBuffer之间的桥梁,消费者直接访问的是SequenceBarrier不是RingBuffer,因此SequenceBarrier能减少RingBuffer的并发冲突

  • workProcessor

处理Event的循环,在循环中获取Disruptor的事件,然后把事件分配给各个handler,消费者就是通过WorkProcessor这个类workHandler.onEvent(event)将事件分配出去的,EventHandler处理器获取并消费

  • EventHandler:

处理器,就是消费者,负责具体的业务逻辑实现

  • WaitStrategy

策略名称原理生产者类型延迟级别CPU使用率适用场景
BlockingWaitStrategy锁阻塞单/多稳定性优先的后台任务
SleepingWaitStrategy自旋 + sleep()单/多异步日志、非实时监控
YieldingWaitStrategy自旋 + yield()单/多高性能交易系统(低延迟)
BusySpinWaitStrategy完全自旋单/多最低100%极端低延迟场景(高频交易)
PassiveWaitStrategy被动等待单/多批量处理任务、宽松的延时要求
TimeoutBlockingWaitStrategy阻塞 + 超时单/多需要超时控制的场景

:选择策略时需权衡延迟、CPU占用及部署复杂度。例如:

  • 金融撮合系统可能优先选择 YieldingBusySpin 策略以满足低延迟;

  • 日志处理系统则适合 Sleeping 策略以平衡资源消耗与性能。

框架结构如下(官方原图)
可以看到都是围绕着RingBuffer来行动的,生产者根据等待策略和配置发送到RingBuffer,消费者通过ProducerBarrier和ConsumerBarrier处理RingBuffer
在这里插入图片描述

官方例子

举一个官方使用的小demo

定义一个事件

public class LongEvent
{
    private long value;

    public void set(long value)
    {
        this.value = value;
    }

    @Override
    public String toString()
    {
        return "LongEvent{" + "value=" + value + '}';
    }
}

定义一个委托事件工厂

import com.lmax.disruptor.EventFactory;

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

定义一个消费者

public class LongEventHandler implements EventHandler<LongEvent>
{
    @Override
    public void onEvent(LongEvent event, long sequence, boolean endOfBatch)
    {
        System.out.println("Event: " + event);
    }
}

好了,初始化并开始调用使用

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.examples.longevent.LongEvent;
import com.lmax.disruptor.examples.longevent.LongEventFactory;
import com.lmax.disruptor.examples.longevent.LongEventHandler;
import com.lmax.disruptor.util.DaemonThreadFactory;

import java.nio.ByteBuffer;

public class LongEventMain
{
    public static void main(String[] args) throws Exception
    {
        LongEventFactory factory = new LongEventFactory();

        int bufferSize = 1024;
        Disruptor<LongEvent> disruptor =
                new Disruptor<>(factory, bufferSize, DaemonThreadFactory.INSTANCE);
        disruptor.handleEventsWith(new LongEventHandler());
        disruptor.start();

        RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
        LongEventProducer producer = new LongEventProducer(ringBuffer);
        ByteBuffer bb = ByteBuffer.allocate(8);
        for (long l = 0; true; l++)
        {
            bb.putLong(0, l);
            producer.onData(bb);
            Thread.sleep(1000);
        }
    }
}

这个是上面用到的生产者的事件封装

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.examples.longevent.LongEvent;

import java.nio.ByteBuffer;

public class LongEventProducer
{
    private final RingBuffer<LongEvent> ringBuffer;

    public LongEventProducer(RingBuffer<LongEvent> ringBuffer)
    {
        this.ringBuffer = ringBuffer;
    }

    public void onData(ByteBuffer bb)
    {
        long sequence = ringBuffer.next(); // <1>
        try
        {
            LongEvent event = ringBuffer.get(sequence); // <2>
            event.set(bb.getLong(0));  // <3>
        }
        finally
        {
            ringBuffer.publish(sequence);
        }
    }
}

这个只是简单的例子,具体还有其他更好用的API,有时间可以慢慢看

多消费者模式

这个戏就多了,跟工作流似的,可以支持多种消费模式

多消费者:并行的、广播模式

//注册多个消费者
disruptor.handleEventsWith(new LongEventHandler(), 
    new LongEventHandler1(), 
    new LongEventHandler2());

多消费者:并行的、消费者组模式

同组内消费者之间互斥,一个事件只会被同组内单个消费者处理,但可以支持多个消费者组,消费者组之间完全隔离,互不影响,代码实现方式有两点不同之处:

  • 消费者需要实现WorkHandler接口,而不是 EventHandler 接口;
  • 使用handleEventsWithWorkerPool设置Disruptor的消费者,而不是handleEventsWith方法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

多个消费者组之间航道执行模式

//注册消费者
disruptor.handleEventsWithWorkerPool(new LongWorkHandler(), new LongWorkHandler(), new LongWorkHandler())        
    .thenHandleEventsWithWorkerPool(new OtherWorkHandler(), new OtherWorkHandler(), new OtherWorkHandler());

在这里插入图片描述

多消费者:链式、菱形、六边形执行模式

在这里插入图片描述

// 链式处理
disruptor.handleEventsWith(new LongEventHandler11())
         .then(new LongEventHandler12());

disruptor.handleEventsWith(new LongEventHandler21())
         .then(new LongEventHandler22());

// 菱形处理
disruptor.handleEventsWith(new LongEventHandler1(), new LongEventHandler2())
         .then(new LongEventHandler3());

// 六边形处理
LongEventHandler handler11 = new LongEventHandler();
LongEventHandler handler12 = new LongEventHandler();
LongEventHandler handler21 = new LongEventHandler();
LongEventHandler handler22 = new LongEventHandler();
LongEventHandler handler3 = new LongEventHandler();

disruptor.handleEventsWith(handler11, handler21);
disruptor.after(handler11).handleEventsWith(handler12);
disruptor.after(handler21).handleEventsWith(handler22);
disruptor.after(handler12, handler22).handleEventsWith(handler3);

源码解读

生产过程

这是发布时调用的next获取下一个序列号的源码

public <A> void publishEvent(EventTranslatorOneArg<E, A> translator, A arg0)
{
    final long sequence = sequencer.next();
    translateAndPublish(translator, sequence, arg0);
}

public long next(int n) {
    // 检查申请的数量必须大于0
    if (n < 1) {
        throw new IllegalArgumentException("n must be > 0");
    }
    // 当前生产者已申请到的最后一个序号
    long nextValue = this.nextValue;
    // 将要申请的最后一个序号(期望申请 n 个)
    long nextSequence = nextValue + n;
    // wrapPoint 是“数据回绕点”,用于判断是否会覆盖未消费数据
    // 比如 RingBuffer size=16,当前nextSequence=20,则 wrapPoint=4
    long wrapPoint = nextSequence - bufferSize;
    // 上次缓存的 gatingSequence(消费者中消费最慢的那个位置)
    long cachedGatingSequence = this.cachedValue;
    // 触发两个场景下的判断:
    // 1. wrapPoint > cachedGatingSequence → 说明可能覆盖未消费数据,需要重新检查
    // 2. cachedGatingSequence > nextValue → 极端情况(比如消费者快于生产者)
    if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue) {
        // 设置 cursor 的 volatile 值,内存屏障,确保生产者修改对消费者可见
        cursor.setVolatile(nextValue); // StoreLoad 屏障
        long minSequence;
        // 自旋等待,直到 wrapPoint <= 最慢消费者的位置,确保不会覆盖数据
        while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue))) {
            // parkNanos 是轻量级自旋,避免 CPU 忙等(等待1纳秒,频繁检查)
            LockSupport.parkNanos(1L); // 实际生产中可替换为更优的 waitStrategy
        }
        // 更新缓存,避免每次都计算 gatingSequences
        this.cachedValue = minSequence;
    }
    // 更新 nextValue,表示当前生产者已经申请到了 nextSequence
    this.nextValue = nextSequence;
    // 返回新分配的最大序号(注意:返回的是末尾序号,通常你自己还要处理 head-tail 区间)
    return nextSequence;
}

获取到下一个序列号会调用translateAndPublish,在finally中publish

private <A> void translateAndPublish(EventTranslatorOneArg<E, A> translator, long sequence, A arg0)
{
    try
    {
        translator.translateTo(get(sequence), sequence, arg0);
    }
    finally
    {
        sequencer.publish(sequence);
    }
}

在这里插入图片描述

这时候就涉及到我们配置的事single还是multi了,好我们看下single

public void publish(long sequence)
{
    cursor.set(sequence);
    waitStrategy.signalAllWhenBlocking();
}

可以看到调用WaitStrategy等待策略了

在这里插入图片描述

我们主要看下BlockingWaitStrategy,可以看到它是通过ReentrantLock锁住保证线程安全的,通过Condition等待-释放策略通知消费端可以消费了

public final class BlockingWaitStrategy implements WaitStrategy
{
    private final Lock lock = new ReentrantLock();
    private final Condition processorNotifyCondition = lock.newCondition();
    
   public void signalAllWhenBlocking()
    {
        lock.lock();
        try
        {
            processorNotifyCondition.signalAll();
        }
        finally
        {
            lock.unlock();
        }
    }
}

消费流程

WorkProcessor负责从 RingBuffer 中消费事件,并通过 WorkHandler 进行处理

WorkProcessor.run() 是一个持续运行的消费循环:

  • 每个 WorkProcessor 是一个独立的工作线程(通常是实现了 Runnable 的类)。
  • 多个 WorkProcessor 共同竞争 workSequence(共享的工作序号)来获取任务的处理权,避免重复消费。
  • 每个线程一旦抢到任务,就从 RingBuffer 中获取事件,执行 WorkHandler.onEvent() 进行业务处理
public void run() {
    boolean processedSequence = true; // 标识上一个事件是否已处理完
    long cachedAvailableSequence = Long.MIN_VALUE; // 缓存可用的最大事件序号
    long nextSequence = sequence.get(); // 当前处理器要处理的下一个序号
    T event = null;

    while (true) {
        try {
            // 如果上一个事件已经处理完成,尝试获取下一个事件处理权
            if (processedSequence) {
                processedSequence = false;

                // 通过 CAS(Compare-And-Set)竞争下一个可处理的序号
                // 保证每个 WorkProcessor 处理唯一的事件,防止重复消费
                do {
                    nextSequence = workSequence.get() + 1L;
                    sequence.set(nextSequence - 1L); // 设置本线程的序号(还没正式消费)
                } while (!workSequence.compareAndSet(nextSequence - 1L, nextSequence));
            }

            // 判断事件是否已发布(数据是否准备好)
            if (cachedAvailableSequence >= nextSequence) {
                // 从 RingBuffer 中获取事件
                event = ringBuffer.get(nextSequence);

                // 调用业务逻辑处理器处理事件
                workHandler.onEvent(event);

                // 标记事件处理完成
                processedSequence = true;
            } else {
                // 当前事件尚未发布,阻塞等待发布
                cachedAvailableSequence = sequenceBarrier.waitFor(nextSequence);
            }
        }
        // catch 块略去,可以处理 AlertException、InterruptedException 等

    }

    // 线程退出前清理操作
    notifyShutdown();

    // 设置运行标志为 false,表明该 WorkProcessor 停止运行
    running.set(false);
}

waitFor() 方法解释(来自 SequenceBarrier):

public long waitFor(final long sequence)
    throws AlertException, InterruptedException, TimeoutException {
    
    checkAlert(); // 检查是否中断或关闭

    // 调用 WaitStrategy(如 BlockingWaitStrategy、BusySpinWaitStrategy 等)等待目标序号可用
    long availableSequence = waitStrategy.waitFor(sequence, cursorSequence, dependentSequence, this);

    if (availableSequence < sequence) {
        return availableSequence; // 数据未准备好,提前返回
    }

    // 过滤掉尚未真正“发布”的数据(Disruptor 会预分配槽位,但未必写完数据)
    return sequencer.getHighestPublishedSequence(sequence, availableSequence);
}
public long getHighestPublishedSequence(long lowerBound, long availableSequence) {
    for (long sequence = lowerBound; sequence <= availableSequence; sequence++) {
        if (!isAvailable(sequence)) {
            return sequence - 1; // 找到第一个未发布的事件,返回上一个可用序
        }
    }
    return availableSequence; // 全部发布了,返回最大值
}

在消费时我们实现了implements EventHandler,然后onEvent方法中消费,但是看源码是调用的WorkHandler的onEvent消费,那么问题来了,他俩啥区别

EventHandlerWorkHandler 的区别是啥?

先汇总下,然后再解释

消费接口模式使用类方法签名是否共享消费事件
EventHandler发布-订阅
(EventProcessor)
BatchEventProcessoronEvent(event, sequence, endOfBatch)不, 所有消费者都收到事件(广播)
WorkHandler<T>工作队列
(WorkHandler)
WorkerPool / WorkProcessoronEvent(event)是的, 多消费者分担处理(每个事件只被一个消费线程处理)

下面可以看到EventProcessor有3个实现类,其中就有这两个使用类

在这里插入图片描述

BatchEventProcessor发布-订阅模式

看下BatchEventProcessor处理上是差不多的

public final class BatchEventProcessor<T>
    implements EventProcessor
  
    private final EventHandler<? super T> eventHandler;
@Override
    public void run()
    {
        if (running.compareAndSet(IDLE, RUNNING))
        {
            sequenceBarrier.clearAlert();

            notifyStart();
            try
            {
                if (running.get() == RUNNING)
                {
                    processEvents();
                }
            }
            finally
            {
                notifyShutdown();
                running.set(IDLE);
            }
        }
    }


private void processEvents()
    {
        T event = null;
        long nextSequence = sequence.get() + 1L;

        while (true)
        {
            try
            {
                final long availableSequence = sequenceBarrier.waitFor(nextSequence);
                if (batchStartAware != null)
                {
                    batchStartAware.onBatchStart(availableSequence - nextSequence + 1);
                }

                while (nextSequence <= availableSequence)
                {
                    event = dataProvider.get(nextSequence);
                    eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence);
                    nextSequence++;
                }

                sequence.set(availableSequence);
            }
        }
}

可以看到最终也是调用了eventHandler.onEvent这个的

典型用途:你想对事件做不同的处理,如日志、分析、持久化,可以同时配置多个 EventHandler

WorkHandler<T> —— 工作队列模式

多个消费者 竞争性消费事件(一个事件只被处理一次),适合“负载均衡”处理任务

典型用途:多个线程并发处理任务,像线程池

其他问题

如何消除伪共享?

先介绍下什么是伪共享,什么问题,如何消除吧,后面在linux的介绍上再写一遍

来张图看下内存、L1,L2,L3缓存和CPU的位置关系

在这里插入图片描述

什么是伪共享问题?

伪共享是 ‌多核CPU架构‌ 中特有的性能问题,当多个线程频繁修改 ‌同一缓存行(Cache Line)中的不同变量‌ 时,会导致 ‌缓存失效风暴‌,引发严重的性能下降,比如

class Data {
    volatile long x;  // 线程A频繁修改
    volatile long y;  // 线程B频繁修改
}

x和y位于同一缓存行(假设起始地址对齐)

假设:有两个线程同时访问并修改X和Y这两个变量,X和Y恰好在同一个缓存行上,这两个线程分别在不同的CPU上执行。

线程A修改x,线程B修改y,由于x被更新了,所以缓存行失效,或者y被更新了,缓存行也失效,会重新去内存中读取数据到L3,本来两个相互不影响,现在会相互影响

程序执行效率明显下降

如何消除?

那自然是好几种,都有填充法(经典)、字段重排序、@Contended注解、线程局部变量这几种

Disruptor用的是经典的填充法空间换时间

原理‌:通过填充无用字段,让x和y分布在不同的缓存行

class Data {
    volatile long x;
    // 填充7个long(56字节)+ 对象头8字节 = 64字节
    private long p1, p2, p3, p4, p5, p6, p7; 
    volatile long y;
}

来看RingBuffer如何做的,可以看到RingBuffer变量INITIAL_CURSOR_VALUE的前后都填充了7个long,一个long8个字节,前后加一起就是56+56=112个字节,一行缓存行时64,完全够用了

前面介绍的Sequence也是一样,就是为了让它自己占用一个内存行,不和其他冲突,减少缓存失效的情况,提高性能

abstract class RingBufferPad{
    protected long p1, p2, p3, p4, p5, p6, p7;
}

abstract class RingBufferFields<E> extends RingBufferPad{}

public final class RingBuffer<E> extends RingBufferFields<E> implements Cursored, EventSequencer<E>, EventSink<E>{
    public static final long INITIAL_CURSOR_VALUE = Sequence.INITIAL_VALUE;
    protected long p1, p2, p3, p4, p5, p6, p7;
}

如何管理消费者和生产者之间的依赖关系

通过SequenceBarrier进行依赖管理,

消费者的processer,通过 SequenceBarrier获取生产者的生产序号

数据覆盖问题如何解决?

gatingSequences:记录每个消费者当前消费到的序号

举例

有环形队列-数组结构如下,有16个位置

       0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15

index: [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]

sequence是累加的数字,生产者准备写的序号是20,那么下一个位置就是20-16=4,超过一圈的第4个位置

但是如果消费者最慢只读到了3,说明4号位置还没有被消费,不能写!

需等待消费者前进:LockSupport.parkNanos(1L); // 轻量级自旋等待

总结:Disruptor用 gatingSequences 追踪最慢的消费者位置,如果最慢位置<要写的位置,则自旋等待,防止覆盖写入

背压问题如何解决?

背压问题就是数据不能覆盖,而消费者速度赶不上生产者就会产生积压,自旋,等待,直到有消费者把老数据消费掉

Disruptor是故意卡住的,因为不能覆盖,那如何解决?

  1. 多加消费者线程(推荐)
  2. 优化消费逻辑,不要阻塞,不要琐(推荐)
  3. 加大RingBuffer(必须是2的幂)
  4. 限速生产者
  5. 异步持久化,分发任务,比如发到MQ中去消费

自旋的问题

LockSupport.parkNanos(1L); 短暂暂停线程,更轻量

风险

  1. 如果积压严重,自旋时间长会浪费CPU时间片
  2. 自旋线程太多可能导致CPU资源被抢占,拖垮系统

如何合理设置RingBuffer容量?

RingBuffer容量越大:能承受的积压越多,占内存也越多;能应对消费抖动

Disruptor 各种模式推荐配置总览表

模式类型推荐消费者数推荐 bufferSize(2的幂)单机 TPS 参考值推荐应用场景
单生产者 + 单消费者11024~819210W+ TPS简洁快速任务、事件驱动、日志过滤、游戏帧更新
单生产者 + 多消费者(WorkPool)4~88192~6553620W~50W TPS高并发数据处理、日志异步处理、风控处理
单生产者 + 多消费者(广播/订阅)2~465536~1310723W~8W TPS(取决于最慢消费)数据同步、多系统广播、审计追踪、多模块分发
多生产者 + 单消费者1≥16384(注意 padding)10W~20W TPS多线程写入场景,日志合并、数据聚合
多生产者 + 多消费者(WorkPool)4~1665536~13107250W+ TPS(需调优)极限吞吐场景,撮合引擎、实时日志平台、交易系统
应用采用模式配置建议
游戏逻辑帧分发单生产 + 单消费bufferSize: 2048,TPS: 1W+,极低延迟
日志异步写盘单生产 + WorkPool消费者数: 4,bufferSize: 16384,TPS: 5W
实时订单风控检测单生产 + 多 WorkHandler消费者数: 8,bufferSize: 65536,TPS: 10W
审计日志广播(分模块处理)单生产 + 广播消费者数: 3,bufferSize: 131072,TPS: 5W
交易撮合系统多生产 + 多 WorkPool消费者数: 16,bufferSize: 131072,TPS: 50W+,需多核调优

当然了,上面这些配置只是参考,我也是从其他地方找来的,大家如果线上要用一定一定一定要经过严格的压测才能得出结论哈

参考:https://mp.weixin.qq.com/s?__biz=MzIyNDU2ODA4OQ==&mid=2247501790&idx=1&sn=e86cea20c7eb8075227aea9be87f97dc&chksm=e80e71a8df79f8be3fe59a81be769a9f669e607e47ec33b786fe7e249fbb32a55b2a5075f185#rd

https://mp.weixin.qq.com/s/1Bxsw3tszrbLqfzPg0wqxQ

生命自有出路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

站长大人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值