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