上篇文章讲到,整个disruptor核心设计就是围绕Ringbuffer展开,本篇文章将会详细的介绍生产者是如何向Ringbuffer中写入数据的,耐心看完,就能理解单个生产者、多个生产者是如何把事件发布到RingBuffer里。
什么是RingBuffer
开始分析生产者流程之前,先简单介绍下什么是Ringbuffer。
其底层采用用数组Object[] entries存储元素,不会扩容,满了之后从数组头开始填充,即所谓环形数组。
在RingBuffer初始化的时候,就用“事件传递器”对象填充entries,初始化后不再新对象 ,消除了不断new对象带来的开销,只对entries中的Event对象做update操作。
同时采用了预填充变量,尽量避免了CPU“伪共享”。
生产者构成
分析之前,简单介绍下生产者的构成
- Event
- "事件传递器"
- EventFactory
- EventHandler
- RingBuffer
- Disruptor
Event,即我们要发布的事件,比如加入购物车、下单等具体的业务操作,内部可能包含请求参数,当前事件类型等;
“事件传递器”,(非官方命名,作者原创)用来填充RingBuffer,生产者和消费者之间的桥梁,生产者把事件交给“事件传递器”,消费者从“事件传递器”获取对应的事件;
EventFactory,工厂类,结合EventTranslator一起用,生产“事件传递器”;
EventHandler,事件处理器,用来处理具体的事件,即消费者,之所以单独列出来,是想让大家分清其和“事件传递器”的区别(刚开始看代码的时候,没绕出来);
Disruptor类,框架入口,创建Disruptor时候要指定一些核心参数,用来创建对应的RingBuffer;
public Disruptor(
final EventFactory<T> eventFactory, // 工厂类
final int ringBufferSize, // ringBuffer的大小
final ThreadFactory threadFactory, // 线程工厂,多个消费者会用到
final ProducerType producerType, // 生产者类型,当前是单生产者,还是多生产者
final WaitStrategy waitStrategy) // 等待策略,当生产速度比较慢的时候,怎么等待新的消息过来
{
this(
RingBuffer.create(producerType, eventFactory, ringBufferSize,waitStrategy),
new BasicExecutor(threadFactory));
}
一次数据生产流程
可以看到,当我们的创建Disruptor的时候,内部会进行RingBuffer的初始化,可以选择是初始化单一生产者,还是多生产的RingBuffer,区别就在于内部记录生产序号的Sequence实现类不同(后面文章会详细介绍)
调用对应的EventFactory的newInstance方法,提前初始化“事件传递器”,用来填充RingBuffer。
RingBufferFields(EventFactory<E> eventFactory, Sequencer sequencer){
... ...
this.entries = new Object[sequencer.getBufferSize() + 2 * BUFFER_PAD];
fill(eventFactory);
}
private void fill(EventFactory<E> eventFactory)
{
for (int i = 0; i < bufferSize; i++)
{
// 环形缓冲区里,每一个节点,放的都是eventFactory.newInstance()对象
// 典型的空间换时间
// BUFFER_PAD, 用于在数组中进行缓存行填充的空元素个数
entries[BUFFER_PAD + i] = eventFactory.newInstance();
}
}
既然RingBuffer内部会提前创建很多“事件传递器”,那么消费者,是怎么把生产的事件,交给环上的“事件传递器”的呢?可以看下对应的发布事件动作。
public <A> boolean tryPublishEvent(EventTranslatorOneArg<E, A> translator, A arg0)
{
try
{
// 取序号,来决定自己要写入到环上的哪个节点里
final long sequence = sequencer.tryNext();
//调用translator,把发布的事件,传递给“事件发布者”
translateAndPublish(translator, sequence, arg0);
return true;
}
catch (InsufficientCapacityException e)
{
return false;
}
}
private <A> void translateAndPublish(EventTranslatorOneArg<E, A> translator, long sequence, A arg0)
{
try
{
// 根据序号,获取到对应的“事件发布器”
// 调用translateTo实现,把要发布的事件,传递给“事件发布器”
translator.translateTo(get(sequence), sequence, arg0);
}
finally
{
// 最后,提交seq,表示,当前的环已经被占用了,后续有事件发布,请选用其他的位置
sequencer.publish(sequence);
}
}
根据序号管理器,首先获取自己能写入到RingBuffer的序号,根据序号,获取自己能写入的“事件发布器”,把对应的事件交给“事件发布器”,提交序号,表明当前“事件发布器”已经被占用。
RingBuffer类图
最后,放上一张RingBuffer类图,可以看到,RingBuffer的核心,其实是Sequenced,通过Sequenced,管理生产的“进度”,同时,也依赖管理Sequenced,实现多消费者高效并行消费,后续文章会详细介绍,勿急~。
小结
想清晰的理解Disruptor的生产流程,关键就要弄明白RingBuffrer里,是怎么存储事件的。
首先创建Disruptor初始化RingBuffer的时候,会根据配置,提前初始化N个“事件传递器”(一般会多于配置数量,缓存行空填充 官方解释)
生产者发布事件的时候,通过“序号管理器”获取到对应的“事件传递器”,把事件交给对应的“传递器”,即发布事件。
更新“序号管理器”,表示这个“传递器”已经被占用,其他生产者生产消息的时候,要请申请其他的“传递器”。