disruptor:3.3.4
jdk:1.8
介紹:
disruptor是典型的生產者和消費者模式,disruptor使用RingBuffer存放數據,sequence管理生產和消費的位置(long類型,遞增),生產者要生產數據的時候首先向生產者請求一個sequence,然後在sequence的位置放置數據。
入門:
生產的數據模型
public class LogEvent {
private long value;
public void setValue(long value) {
this.value = value;
}
public String toString() {
return this.value + "";
}
}
消費者消費數據
public class EventHandler {
public void handleEvent(LogEvent event, long sequence, boolean falg) throws Exception {
System.out.println("handle event " + event + " cur thread " + Thread.currentThread().getId());
}
}
初始化:
void sigleProducerSingleConsume() throws Exception {
int bufferSize = 8; // RingBuffer的大小,2的n次方
ThreadFactory threadFactory = Executors.defaultThreadFactory();
EventHandler handler = new EventHandler();
Disruptor<LogEvent> disruptor = new Disruptor<>(LogEvent::new, bufferSize, threadFactory, ProducerType.SINGLE, new BlockingWaitStrategy());
disruptor.handleEventsWith(handler::handleEvent);
RingBuffer<LogEvent> buffer = disruptor.start();
for(int i=0; i<10; i++) {
long sequence = buffer.next(); // 申請位置
try {
LogEvent event = buffer.get(sequence);
event.setValue(i); // 放置數據
} finally {
buffer.publish(sequence); // 提交,如果不提交完成事件會一直阻塞
}
}
disruptor.shutdown();
}
原理解析:
多個生產者模式:
Disruptor<LogEvent> disruptor = new Disruptor<>(LogEvent::new, bufferSize, threadFactory, ProducerType.MULTI, new BlockingWaitStrategy());
初始化方法:
public Disruptor(
final EventFactory<T> eventFactory,
final int ringBufferSize,
final ThreadFactory threadFactory,
final ProducerType producerType,
final WaitStrategy waitStrategy)
{
this(RingBuffer.create(
producerType, eventFactory, ringBufferSize, waitStrategy),
new BasicExecutor(threadFactory));
}
創建RingBuffer:
public static <E> RingBuffer<E> create(
ProducerType producerType,
EventFactory<E> factory,
int bufferSize,
WaitStrategy waitStrategy)
{
switch (producerType)
{
case SINGLE:
return createSingleProducer(factory, bufferSize, waitStrategy);
case MULTI:
return createMultiProducer(factory, bufferSize, waitStrategy);
default:
throw new IllegalStateException(producerType.toString());
}
}
public static <E> RingBuffer<E> createMultiProducer(
EventFactory<E> factory,
int bufferSize,
WaitStrategy waitStrategy)
{
MultiProducerSequencer sequencer = new MultiProducerSequencer(bufferSize, waitStrategy);
return new RingBuffer<E>(factory, sequencer);
}
這裏可以看到,使用MultiProducerSequencer 創建了RingBuffer,當我們使用單個生產者模式的時候可以看到使用SingleProducerSequencer創建了RingBuffer,所以,disruptor是使用一個sequence來維護生產者用到的index 的。
讓我們看看MultiProducerSequencer 的代碼:
public MultiProducerSequencer(int bufferSize, final WaitStrategy waitStrategy)
{
super(bufferSize, waitStrategy);
availableBuffer = new int[bufferSize];
indexMask = bufferSize - 1;
indexShift = Util.log2(bufferSize);
initialiseAvailableBuffer();
}
要注意availableBuffer ,這個數組中保存了生產者在每個位置添加數據的信息。
indexShift :bufferSize >> indexShift + 1 時爲0
initialiseAvailableBuffer()初始化availableBuffer 所有元素爲-1
然後讓我們看添加數據的處理:
long sequence = buffer.next(); // 申請位置
try {
LogEvent event = buffer.get(sequence);
event.setValue(i); // 放置數據
} finally {
buffer.publish(sequence); // 提交,如果不提交完成事件會一直阻塞
}
取得可用的位置
@Override
public long next()
{
return next(1);
}
/**
* @see Sequencer#next(int)
*/
@Override
public long next(int n)
{
if (n < 1)
{
throw new IllegalArgumentException("n must be > 0");
}
long current;
long next;
do
{
current = cursor.get(); //
next = current + n; // 獲取下一個可用位置,最好不要用next(n)來獲取位置,很容易產生死循環
long wrapPoint = next - bufferSize; //
long cachedGatingSequence = gatingSequenceCache.get(); // 保存了消費者處理過的最小位置
// 保證生產者沒有超過消費者一圈。
if (wrapPoint > cachedGatingSequence || cachedGatingSequence > current)
{
long gatingSequence = Util.getMinimumSequence(gatingSequences, current);
if (wrapPoint > gatingSequence)
{
LockSupport.parkNanos(1); // TODO, should we spin based on the wait strategy?
continue;
}
gatingSequenceCache.set(gatingSequence);
}
else if (cursor.compareAndSet(current, next))
{
break;
}
}
while (true);
return next;
}
中心思想就是獲取當前可用的位置,這裏沒有用到鎖,而是使用了cas。
gatingSequence中保存了各個消費者處理過的當前位置。
這樣當前生產者就可以使用這個位置了,最後要記得publish,告知消費者去讀取數據。
@Override
public void publish(final long sequence)
{
setAvailable(sequence); //
waitStrategy.signalAllWhenBlocking(); // 消費者可以讀了
}
setAvailable是一個很巧妙的設計,如果是多個生產者,availableBuffer中的值是這樣變化的:
[-1, -1, -1, -1]
[0, -1, -1, -1]
[0, 0, 0, 0]
至此,四個位置已經被寫入數據了
[1, 0, 0, 0]
[1, 1, 0, 0]
[1, 1, 0, 1]
[1, 1, 1, 1]
看第三種情形,producer1佔據了第三個位置,但是producer2先一步在第四個位置寫入了數據,consumer是可以跳過第三個位置讀取第四個位置的數據的。但是這種情況會極少出現,第三個位置沒有沒consumer讀取的話,producer2是不能拿到第四個位置的。
private void setAvailable(final long sequence)
{
setAvailableBufferValue(calculateIndex(sequence), calculateAvailabilityFlag(sequence));
}
private int calculateAvailabilityFlag(final long sequence)
{
return (int) (sequence >>> indexShift);
}
private int calculateIndex(final long sequence)
{
return ((int) sequence) & indexMask;
}
private void setAvailableBufferValue(int index, int flag)
{
long bufferAddress = (index * SCALE) + BASE;
UNSAFE.putOrderedInt(availableBuffer, bufferAddress, flag);
}