Disruptor笔记(四)-关键类和代码

AggregateEventHandler.java

对EventHandler列表的封装,类似EventHandler List的功能,还实现了生命周期的管理,onStart onShutdown。

 

Sequence.java

Cache line padded sequence counter 补齐Cache line的序列计数器,ringbuffer和BatchEventProcessor使用到此类来计数。

补齐方式:

[java]  view plain copy
  1.   
    public long  p1 ,  p2 ,  p3 ,  p4 ,  p5 ,  p6 ,  p7 ; // cache line padding,  padding1      private volatile long  cursor  =  INITIAL_CURSOR_VALUE

    publiclong p8, p9, p10, p11, p12, p13, p14;// cache line padding.  padding2

   情形1: object(0~8byte)+ padding1,    

                  cursor+padding2

   情形2: padding1+ cursor,

                  padding2 + object

这样,保证不同Sequence instance在不同的Cache line

 参考资料:http://mechanical-sympathy.blogspot.com/2011/07/false-sharing.html

因为高速缓存是64字节,而Hotspot JVM的对象头是两个部分组成,第一部分是由24字节的hash code和8字节的锁等状态标识组成,第二部分是指向该对象类的引用。数组Array有一个附加的"word"来记录数组长度。每个对象为了性能优化,采用8个byte粒度边界对齐的。为了在packing的时候更高效,对象的field被从定义顺序(基于字节大小)按下列顺序重排:

1.doubles(8) and longs(8)

2.ints(4) and floats(4)

3.shorts(2) and chars(2)

4.booleans(1) and bytes(1)

5.references(4/8)

6.<repeat for sub-class fields>

所以我们补齐cache line:在任意field之间补上7个long(8)

BatchEventProcessor.java 批量从RingBuffer获取event代理給EventHandler处理。

关键代码:

[java]  view plain copy
  1. public void run()  
  2.     {  
  3.         if (!running.compareAndSet(falsetrue))  
  4.         {  
  5.             throw new IllegalStateException("Thread is already running");  
  6.         }  
  7.         sequenceBarrier.clearAlert();  
  8.    
  9.         notifyStart();  
  10.    
  11.         T event = null;  
  12.         long nextSequence = sequence.get() + 1L;  
  13.         while (true)  
  14.         {  
  15.             try  
  16.             {  
  17.                 final long availableSequence = sequenceBarrier.waitFor(nextSequence);  
  18.                  //批量处理,nextSequence无限增长怎么办?  
  19.                 while (nextSequence <= availableSequence)  
  20.                 {  
  21.                     event = ringBuffer.get(nextSequence);  
  22.                     eventHandler.onEvent(event, nextSequence,nextSequence == availableSequence);  
  23.                     nextSequence++;  
  24.                 }  
  25.    
  26.                 sequence.set(nextSequence - 1L);//注意回退1,标示(nextSequence - 1L)的event已经消费完成  
  27.             }  
  28.             catch (final AlertException ex)  
  29.             {  
  30.                if (!running.get())  
  31.                {  
  32.                    break;  
  33.                }  
  34.             }  
  35.             catch (final Throwable ex)  
  36.             {  
  37.                 exceptionHandler.handleEventException(ex,nextSequence, event);//异常处理类处理异常信息  
  38.                 sequence.set(nextSequence);//跳过异常信息的序列  
  39.                 nextSequence++;  
  40.             }  
  41.         }  
  42.    
  43.         notifyShutdown();  
  44.    
  45.         running.set(false);  
  46.     }  


 

                         

ClaimStrategy.java

Sequencer里面的、用于event publishers申请event序列的策略合同。

有以下3种实现:

SingleThreadedClaimStrategy.java: 针对发布者的策略的单线程实现,只能在单线程做publisher的场景使用。

 

关键方法:

[java]  view plain copy
  1. // availableCapacity 需要申请的可用数量  
  2. // dependentSequences 依赖的序列  
  3. public boolean hasAvailableCapacity(final int availableCapacity, final Sequence[] dependentSequences)  
  4.     {  
  5.         final long wrapPoint = (claimSequence.get() + availableCapacity) - bufferSize;//当前已经作为发布使用的序列(未被消费)+申请数量-  
  6.         if (wrapPoint > minGatingSequence.get())  
  7.         {  
  8.             long minSequence = getMinimumSequence(dependentSequences);  
  9. //取出依赖序列中的最小的序列(未被消费)  
  10.             minGatingSequence.set(minSequence);  
  11.    
  12.             if (wrapPoint > minSequence)  
  13.             {  
  14.                 return false;//如果期望的到达的序列位置大于依赖序列中的最小的序列(未被消费),说明尚未消费,所以没有可用序列用于给发布者分配  
  15.             }  
  16.         }  
  17.    
  18.         return true;  
  19.     }  
  20.    
  21. private void waitForFreeSlotAt(final long sequence, final Sequence[] dependentSequences)  
  22.     {  
  23.         final long wrapPoint = sequence - bufferSize;  
  24.         if (wrapPoint > minGatingSequence.get())  
  25.         {  
  26.             long minSequence;  
  27.             while (wrapPoint > (minSequence = getMinimumSequence(dependentSequences)))  
  28.             {  
  29.                 LockSupport.parkNanos(1L);//等待1纳秒  
  30.             }  
  31.    
  32.             minGatingSequence.set(minSequence);  
  33.         }  
  34. }  


 

 

 

MultiThreadedClaimStrategy.java

[java]  view plain copy
  1. @Override  
  2.     public long incrementAndGet(final Sequence[] dependentSequences)  
  3.     {  
  4.         final MutableLong minGatingSequence = minGatingSequenceThreadLocal.get();  
  5.         waitForCapacity(dependentSequences,minGatingSequence);//什么技巧?  
  6.    
  7.         final long nextSequence = claimSequence.incrementAndGet();  
  8.         waitForFreeSlotAt(nextSequence,dependentSequences, minGatingSequence);  
  9.    
  10.         return nextSequence;  
  11.     }  
  12.    
  13.     @Override  
  14.     public long incrementAndGet(final int delta, final Sequence[] dependentSequences)  
  15.     {  
  16.         final long nextSequence = claimSequence.addAndGet(delta);  
  17.         waitForFreeSlotAt(nextSequence,dependentSequences, minGatingSequenceThreadLocal.get());  
  18.    
  19.         return nextSequence;  
  20. }  
  21.    
  22.    
  23.    
  24.    
  25. @Override  
  26. public void serialisePublishing(final long sequence, final Sequence cursor, final int batchSize)  
  27.     {  
  28.         int counter = RETRIES;  
  29.         while (sequence - cursor.get() > pendingPublication.length())  
  30.         {  
  31.             if (--counter == 0)  
  32.             {  
  33.                 Thread.yield();  
  34.                 counter = RETRIES;  
  35.             }  
  36.         }  
  37.    
  38.         long expectedSequence = sequence - batchSize;  
  39.         for (long pendingSequence = expectedSequence + 1;pendingSequence <= sequence; pendingSequence++)  
  40.         {  
  41.             pendingPublication.set((int) pendingSequence& pendingMask, pendingSequence);  
  42.         }  
  43.    
  44.         long cursorSequence = cursor.get();  
  45.         if (cursorSequence >= sequence)  
  46.         {  
  47.             return;  
  48.         }  
  49.    
  50.         expectedSequence = Math.max(expectedSequence,cursorSequence);  
  51.         long nextSequence = expectedSequence + 1;  
  52.         while (cursor.compareAndSet(expectedSequence, nextSequence))  
  53.         {  
  54.             expectedSequence = nextSequence;  
  55.             nextSequence++;  
  56.             if (pendingPublication.get((int) nextSequence & pendingMask) != nextSequence)  //这里是什么含义?只有当nextSequence 大于 PendingBufferSize才会出现不相等的情况。  
  57.             {  
  58.                 break;  
  59.             }  
  60.         }  
  61.     }  


 

 

MultiThreadedLowContentionClaimStrategy.java

与MultiThreadedClaimStrategy.java的在于:

[java]  view plain copy
  1. @Override  
  2.     public void serialisePublishing(final long sequence, final Sequence cursor, final int batchSize)  
  3.     {  
  4.         final long expectedSequence = sequence - batchSize;  
  5.         while (expectedSequence != cursor.get())//会不会死循环?  
  6.         {  
  7.             // busy spin  
  8.         }  
  9.    
  10.         cursor.set(sequence);  
  11.     }  
  12.    


 

EventPublisher.java

时间发布者,主要代码:


 

 

 

 

 

[java]  view plain copy
  1. private void translateAndPublish(final EventTranslator<E> translator, final long sequence)  
  2.     {  
  3.         try  
  4.         {  
  5.             translator.translateTo(ringBuffer.get(sequence),sequence);//需要根据传入的translator来依据sequence转换event后,再发布event.  
  6.         }  
  7.         finally  
  8.         {  
  9.             ringBuffer.publish(sequence);  
  10.         }  
  11. }  
  12.    

WaitStrategy.java

定制EventProcessor等待cursor这个sequence的策略,有以下4种实现:

/**

 * Blocking strategy that uses a lock andcondition variable for {@linkEventProcessor}s waiting on a barrier.

 *

 * This strategy can be used when throughputand low-latencyare not as important as CPU resource.

 */

BlockingWaitStrategy.java:用到了lock,所以只适合用在throughput和low-latency要求不高的情况下。

 

/**

 * Busy Spin strategy that uses a busy spinloop for {@link com.lmax.disruptor.EventProcessor}s waiting on a barrier.

 *

 * This strategy will use CPU resource to avoidsyscalls which can introduce latency jitter. It is best

 * used when threads can be bound to specificCPU cores.

 */

BusySpinWaitStrategy.java:这种是耗cpu的做法,不做yield()。

 

/**

 * Sleeping strategy that initially spins, thenuses a Thread.yield(), and eventually for the minimum number of nanos

 * the OS and JVM will allow while the {@link com.lmax.disruptor.EventProcessor}s are waiting on a barrier.

 *

 * This strategy is a good compromise betweenperformance and CPU resource. Latency spikes can occur after quiet periods.

 */

SleepingWaitStrategy.java:做一个counter的判断,小于100才yield(),小于0做LockSupport.parkNanos(1L);

/**

 * Yielding strategy that uses a Thread.yield()for {@link com.lmax.disruptor.EventProcessor}s waiting on a barrier

 * after an initially spinning.

 *

 * This strategy is a good compromise betweenperformance and CPU resource without incurring significant latency spikes.

 */

YieldingWaitStrategy.java:counter==0,才做yield()。

你可能感兴趣的:(Disruptor笔记(四)-关键类和代码)