Disruptor启动过程源码分析(三)

本文深入分析Disruptor的启动过程,包括disruptor.start()方法、ConsumerRepository如何获取gatingSequences、RingBuffer的工作原理以及生产者如何发布数据。通过源码解析,阐述了Disruptor如何协调生产者和消费者,以及其高性能的等待策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接着  Disruptor启动过程源码分析(二)

现在我们来看看Main.java的第三步:disruptor.start(),点开start方法看看

    public RingBuffer<T> start()
    {
        final Sequence[] gatingSequences = consumerRepository.getLastSequenceInChain(true);
        ringBuffer.addGatingSequences(gatingSequences);

        checkOnlyStartedOnce();
        for (final ConsumerInfo consumerInfo : consumerRepository)
        {
            consumerInfo.start(executor);
        }

        return ringBuffer;
    }

这里有我们上一张进行操作的gatingSequences字段,看看是如何获取的,点开getLastSequenceInChain方法看看

   public Sequence[] getLastSequenceInChain(boolean includeStopped)
    {
        //1.
        List<Sequence> lastSequence = new ArrayList<Sequence>();
        //2.
        for (ConsumerInfo consumerInfo : consumerInfos)
        {
            //3.
            if ((includeStopped || consumerInfo.isRunning()) && consumerInfo.isEndOfChain())
            {
                //4.
                final Sequence[] sequences = consumerInfo.getSequences();
                Collections.addAll(lastSequence, sequences);
            }
        }
        //5.
        return lastSequence.toArray(new Sequence[lastSequence.size()]);
    }

首先这个ComsumerRepository对象不知大家还是否记得,就是在上一章执行 

disruptor.handleEventsWith(new OrderEventHandler());

的时候,handler最后被add进的地方。

我们看看方法

1、创建一个字段名叫lastSequence的List集合

2、遍历所有的ConsumerInfo(这里的ConsumerInfo可以理解为handler的包装类)

3、4、如果handler正在运行,并且他是处理链中的最后一个则添加进行集合中

5、返回lastSequence集合

应该可以这样理解:每次往ComsumerRepository添加handler,都会更新endOfChain字段用于标记是不是最后一个,然后往gatingSequence里面添加需要处理的sequence,这里做的原因应该是避免重复处理消费者。

 

回到start方法中,之后我们得到了所要消费的消费者,然后对线程池进行start操作,截止目前我们还没生,所以没有消费者进行消费。

 

回到Main.java类我们继续往下走

下面我们是得到了disruptor里面的RingBuffer,开始往里面增加一些生产者生产一些数据。我们实例化好OrderEventProduct后,开始进入我们自己写的sendData方法中

 public void sendData(ByteBuffer data) {
        //1 在生产者发送消息的时候, 首先 需要从我们的ringBuffer里面 获取一个可用的序号
        long sequence = ringBuffer.next();    //0
        try {
            //2 根据这个序号, 找到具体的 "OrderEvent" 元素 注意:此时获取的OrderEvent对象是一个没有被赋值的"空对象"
            OrderEvent event = ringBuffer.get(sequence);
            //3 进行实际的赋值处理
            event.setValue(data.getLong(0));
        } finally {
            //4 提交发布操作
            ringBuffer.publish(sequence);
        }
    }

这个方法用于向Disruptor里RingBuffer环形数组中添加对象供于handler消费。

首先看看RingBuffer.next()方法

    public long next()
    {
        return sequencer.next();
    }

这里调用的是sequencer中的next(),上门已经知道,sequencer是用于存放消费者序号的,继续往下看来到实现类SingleProductSequencer中

 public long next()
    {
        return next(1);
    }

这里是获取下1个序列,继续

 public long next(int n)
    {
        //1、如果索引小于1或者大于内存长度则抛异常
        if (n < 1 || n > bufferSize)
        {
            throw new IllegalArgumentException("n must be > 0 and < bufferSize");
        }

        //2、获取下一个值
        long nextValue = this.nextValue;
        //3、获取下一个索引
        long nextSequence = nextValue + n;
        //4、当前实际序列,比如当前是11 ,size为10  则wrapPoint = 11-10 = 1
        long wrapPoint = nextSequence - bufferSize;
        //5、最小缓存序列
        long cachedGatingSequence = this.cachedValue;
        //6、如果wrapPoint大于最小缓存序列或者最小缓存序列大于下一个值,说明消费者过快了需要等一等
        if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
        {
            //7、直接从内存中设置nextValue的值 ,把value设置进valueoffset (值偏移量?)
            cursor.setVolatile(nextValue);  

            long minSequence;
            //8、当前序列大于最小序列,自旋
            while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue)))
            {
                //8、等待1纳秒
                LockSupport.parkNanos(1L); 
            }
            //9、缓存序列为当前最小序列
            this.cachedValue = minSequence;
        }

        this.nextValue = nextSequence;
        //10、返回最小序列
        return nextSequence;
    }

因为RingBuffer是一个环形数组,如果序号大于环形数组的长度则用nextSequence-bufferSize得到下一个序列来覆盖旧的序列的值。上面的代码主要是为了协调生产者线程和消费者线程的速率问题。

我们现在走出next()方法,重新回到自己写的OrderEventProducer#sendData方法中

经过next方法拿到long类型的sequence是当前RingBuffer中最小的空可用序列,然后我们接下来拿到这个序列号对应的“空”对象,这里的空前面已经解释过了,是没有被赋值的对象,然后我们直接get序列值拿到并且填充对象。

最后一步是提交发布(publish)操作,我们看看里面干了什么

首先来到RingBuffer里面的publish方法

 public void publish(long sequence)
    {
        sequencer.publish(sequence);
    }

调用的是Sequencer接口的父类接口Sequenced的publish方法,我们点开其其中一个实现类SingleProducerSequencer看看实现

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

可以看到这里只做了两件事情

1、设置指针为当前序列

2、唤醒所有堵塞中的等待策略。作用大致是当消费者消费过快的时候会以某种配置的等待策略进行等待,目前来看性能最好的是YieldingWatingStrategy,它是一种无锁的等待策略,主要原理是自旋+yield操作。有空会专门写博客去看看源码。

 

每当一个数据进行publish之后,消费者会对其立即进行消费,然后等待新的数据过来,当所有数据被消费完成之后当然就是对disruptor和线程池的shutdown操作,这里就不继续看下去了。

 

好了,Disruptor启动过程就分析完了

如有不正确的地方,请指出我会及时改正,谢谢大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值