@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;
}
}