天天看點

RateLimiter 的底層實作是啥?

RateLimiter 的底層實作是啥?

按圖實作

令牌桶的圖,網上到處可見,按圖實作也非常簡單,無非是定時添加令牌桶,并提供一個擷取令牌的函數,部落客實作了一遍代碼如下:

import java.util.concurrent.*;

public class MyRateLimiter {
    //令牌桶
    BlockingQueue<Integer>TOKEN_BUCKET=new LinkedBlockingDeque<>(5);

    public static void main(String[] args) {
        MyRateLimiter myRateLimiter=new MyRateLimiter();
        myRateLimiter.addTokenFixedRate();
       for(int i=0;i<10;i++){
                myRateLimiter.acqurie();
                System.out.println("第幾次執行i:" + i + ",執行時間為:" + System.currentTimeMillis());
        }
    }
   /**
    * 定時添加令牌
    */
    public void addTokenFixedRate(){
        ScheduledExecutorService scheduledExecutorService= Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleAtFixedRate(()->{
                    boolean suc=TOKEN_BUCKET.offer(1);
                    if(!suc){
                        System.out.println("令牌桶滿了丢棄");
                    }
        },0,200,TimeUnit.MILLISECONDS);
    }

    public void acqurie(){
        while (TOKEN_BUCKET.poll()==null){};
    }

}      

測試結果如下,基本滿足要求

RateLimiter 的底層實作是啥?

RateLimiter概要實作

我一開始是按照自己實作的邏輯,去檢視Guava的RateLimiter的源碼的,結果發現RateLimiter根本沒有集合充當桶,核心是記錄了下一令牌産生的時間與現存令牌數,并動态更新它們。

概要邏輯圖如下:

RateLimiter 的底層實作是啥?

按照這個圖看核心代碼就比較容易了,摘錄核心代碼如下:

@CanIgnoreReturnValue
public double acquire(int permits) {
  long microsToWait = reserve(permits);
  stopwatch.sleepMicrosUninterruptibly(microsToWait);
  return 1.0 * microsToWait / SECONDS.toMicros(1L);
}
//Reserve 一路向下能查到如下代碼  reserveEarliestAvailable

final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
    resync(nowMicros);
    long returnValue = nextFreeTicketMicros;
    // 現存令牌可以提供的令牌數
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    //需要重新整理的令牌數
    double freshPermits = requiredPermits - storedPermitsToSpend;
    //等待時間=需要重新整理的令牌數*固定間隔+存儲許可等待時間
    long waitMicros =
        storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
            + (long) (freshPermits * stableIntervalMicros);
    //下次令牌生産時間=本次令牌生産時間+等待時間
    this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
    this.storedPermits -= storedPermitsToSpend;
    return returnValue;
}      

總結:RateLimiter根本沒有集合充當桶,核心是記錄了下一令牌産生的時間與現存令牌數,并動态更新它們。

繼續閱讀