guava RateLimiter源碼解讀

 @CanIgnoreReturnValue
 //permits是此線程這次要取的令牌數
  public double acquire(int permits) {
  //返回獲得這些數量的令牌需要等待的時間
    long microsToWait = reserve(permits);
    //等待此段時間,期間不響應中斷
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    //返回等待了的微秒數
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
  }
//保存指定數量的permits留作後面的使用,返回在可使用之前經歷的微秒數
final long reserve(int permits) {
//簡單檢查一下permits爲正整數
    checkPermits(permits);
    //做同步
    synchronized (mutex()) {
      return reserveAndGetWaitLength(permits, stopwatch.readMicros());
    }
  }
//保留後面的令牌,返回調用者等待的時間
final long reserveAndGetWaitLength(int permits, long nowMicros) {
//返回令牌最快可用的時間
    long momentAvailable = reserveEarliestAvailable(permits, nowMicros);
    //保證不爲負數
    return max(momentAvailable - nowMicros, 0);
  }
 final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
 //用當前時間先校正一下
    resync(nowMicros);
    long returnValue = nextFreeTicketMicros;
    //可消費的令牌數,取需要的令牌數與已有令牌數之間最小值
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    //還需令牌數=需要令牌數-可消費令牌數
    double freshPermits = requiredPermits - storedPermitsToSpend;
    //對於smoothbursty模式,+號左邊直接返回0,因爲只有預熱機制才需要再對已有令牌等待,右邊就是還需令牌數*每個間隔時間
    long waitMicros =
        storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
            + (long) (freshPermits * stableIntervalMicros);
	//當前時間+要等待時間就是下次可用令牌時間
    this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
    //將要消耗掉的令牌減去
    this.storedPermits -= storedPermitsToSpend;
    return returnValue;
  }
 void resync(long nowMicros) {
    // if nextFreeTicket is in the past, resync to now
    //如果下次可用令牌時間已經早於當前時間則設置爲當前時間
    if (nowMicros > nextFreeTicketMicros) {
    //那麼新多出來的令牌就=(當前時間-可用令牌時間)/生成一個令牌間隔時間
      double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();
      //當前持有令牌數不能超過最大限制令牌數
      storedPermits = min(maxPermits, storedPermits + newPermits);
      //下次可用時間設置成當前時間
      nextFreeTicketMicros = nowMicros;
    }
  }

整體代碼非常簡單,主要通過維護下次可獲取令牌時間nextFreeTicketMicros 來控制速率,storedPermits維護已積累的令牌數,計算時間的時候將reserveEarliestAvailable方法用同步塊控制,保證了一次只有一個線程進入,達到了排隊的效果,對於每個線程得到各自按排隊順序獲得令牌等待相對應的時間。

而對於smoothwarmup模式,它實現了機器剛開始運行時的一個“熱身”模式,逐漸地讓流量放大,直到達到恆定,它與smoothbursty模式的區別在於對storedPermitsToWaitTime方法的實現上:

@Override
//這裏只是計算消費已有令牌需要等待的時間,並不包括新生成令牌等待時間
    long storedPermitsToWaitTime(double storedPermits, double permitsToTake) {
    //富餘可用的令牌數 = 已有令牌數-臨時最大令牌數
      double availablePermitsAboveThreshold = storedPermits - thresholdPermits;
      long micros = 0;
      // measuring the integral on the right part of the function (the climbing line)
      //超出了臨時最大令牌數,那麼要分兩部分計算,一個在穩定區間的時間,一個在熱身前區間的時間
      //進入恆定狀態後,不會富餘令牌,availablePermitsAboveThreshold >0.0不成立
      if (availablePermitsAboveThreshold > 0.0) {
      //在穩定期間要獲得的令牌數
        double permitsAboveThresholdToTake = min(availablePermitsAboveThreshold, permitsToTake);
        // TODO(cpovirk): Figure out a good name for this variable.
        //一個梯形計算面積過程,上邊是穩定後令牌之間時間間隔,下邊是熱身前令牌之間時間間隔
        //高是還需要的令牌數
        double length = permitsToTime(availablePermitsAboveThreshold)
                + permitsToTime(availablePermitsAboveThreshold - permitsAboveThresholdToTake);
        //獲得超出臨時最大令牌數所需時間
        micros = (long) (permitsAboveThresholdToTake * length / 2.0);
        //減掉上面步驟的令牌數,得到還需stable區間所需令牌數
        permitsToTake -= permitsAboveThresholdToTake;
      }
      // measuring the integral on the left part of the function (the horizontal line)
      //加上stable區間等待時間
      micros += (stableIntervalMicros * permitsToTake);
      return micros;
    }

    @Override
    //在構造ratelimiter時調用
    void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
      double oldMaxPermits = maxPermits;
      //coldIntervalMicros:熱身前每個令牌生成之間間隔
      //permitsPerSecond每秒生成令牌數,比如2
      //stableIntervalMicros:穩定後每個令牌生成之間間隔,1/permitsPerSecond,比如0.5s
      //coldFactor:冷卻因子,比如默認是3
      //那麼熱身前每個令牌生成之間間隔coldIntervalMicros就是0.5*3=1.5s
      double coldIntervalMicros = stableIntervalMicros * coldFactor;
      //thresholdPermits :熱身前允許存在的最大令牌數
      //warmupPeriodMicros :總預熱時間,比如5s
      //那麼臨時最大令牌數thresholdPermits  = 0.5*5/0.5 = 5
      thresholdPermits = 0.5 * warmupPeriodMicros / stableIntervalMicros;
      //最大令牌數=5+2.0*5/(0.5+1.5)=10
      maxPermits =
          thresholdPermits + 2.0 * warmupPeriodMicros / (stableIntervalMicros + coldIntervalMicros);
          //slope  = (1.5-0.5)/(10-5) = 0.2
      slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits - thresholdPermits);
      if (oldMaxPermits == Double.POSITIVE_INFINITY) {
        // if we don't special-case this, we would get storedPermits == NaN, below
        storedPermits = 0.0;
      } else {
        storedPermits =
            (oldMaxPermits == 0.0)
                ? maxPermits // initial state is cold
                : storedPermits * maxPermits / oldMaxPermits;
      }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章