前言
。
一、预热原理
X轴:令牌桶中的令牌数量
Y轴:生产一个令牌消耗的时间,单位是s
stableInterval: 稳定生产一个令牌消耗的时间
coldInterval :冷启动生产一个令牌需要的最大时间,与冷却因子coldFactor有关
thresholdPermits:开启预热的令牌阈值
maxPermits:令牌桶最大令牌数
stable:平稳生产thresholdPermits个令牌的时间
warm up period:预热时间
当系统刚启动或者长时间没有收到请求时处于冷却状态,这时令牌达到为 maxPermits;
当有慢慢有请求过来时,存在一个预热期,在预热期间获取令牌的时间会比平稳期获取令牌的时间要长,随着令牌的减少,获取单个令牌的时间会慢慢变短,最终到达一个稳定值 stableInterval
二、示例
@Test
public void warmupRateLimiterTest() {
//每秒生成5个许可,预热时间10s
RateLimiter rateLimiter = RateLimiter.create(5, 10, TimeUnit.SECONDS);
//获取许可,可能会等待
rateLimiter.acquire(1);
//获取许可,可能会等待超时
rateLimiter.tryAcquire(1, 200, TimeUnit.MILLISECONDS);
}
三、创建流程
1、create( )
public static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit) {
checkArgument(warmupPeriod >= 0, "warmupPeriod must not be negative: %s", warmupPeriod);
return create(
permitsPerSecond, warmupPeriod, unit, 3.0, SleepingStopwatch.createFromSystemTimer());
}
static RateLimiter create(
double permitsPerSecond,
long warmupPeriod,
TimeUnit unit,
double coldFactor,
SleepingStopwatch stopwatch) {
//创建SmoothWarmingUp 预热限流器
RateLimiter rateLimiter = new SmoothWarmingUp(stopwatch, warmupPeriod, unit, coldFactor);
rateLimiter.setRate(permitsPerSecond);
return rateLimiter;
}
2、SmoothWarmingUp 构造方法
SmoothWarmingUp(
SleepingStopwatch stopwatch, long warmupPeriod, TimeUnit timeUnit, double coldFactor) {
//计时器
super(stopwatch);
//预热时间
this.warmupPeriodMicros = timeUnit.toMicros(warmupPeriod);
//冷却因子,默认是3
this.coldFactor = coldFactor;
}
3、rateLimiter.setRate( )
public final void setRate(double permitsPerSecond) {
checkArgument(
permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive");
synchronized (mutex()) {
//设置流速
doSetRate(permitsPerSecond, stopwatch.readMicros());
}
}
3、doSetRate( )
@Override
final void doSetRate(double permitsPerSecond, long nowMicros) {
//计算许可,更新下次免费获取许可时间
resync(nowMicros);
//稳定生产一个许可需要的时间
double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;
this.stableIntervalMicros = stableIntervalMicros;
//设置速率
doSetRate(permitsPerSecond, stableIntervalMicros);
}
4、resync(nowMicros)
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;
}
double coolDownIntervalMicros() {
//预热时间 / 最大许可数
return warmupPeriodMicros / maxPermits;
}
5、doSetRate( )
void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
double oldMaxPermits = maxPermits;
//coldInterval,预热生产一个令牌需要的最大时间
double coldIntervalMicros = stableIntervalMicros * coldFactor;
//许可阈值
thresholdPermits = 0.5 * warmupPeriodMicros / stableIntervalMicros;
//许可最大值
maxPermits =
thresholdPermits + 2.0 * warmupPeriodMicros / (stableIntervalMicros + coldIntervalMicros);
//斜率
slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits - thresholdPermits);
//初始化存储的许可数 storedPermits
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;
}
}
四、获取流程
1、acquire(int permits)
public double acquire(int permits) {
//计算需要等待的时间
long microsToWait = reserve(permits);
//休眠等待
stopwatch.sleepMicrosUninterruptibly(microsToWait);
//返回等待的时间
return 1.0 * microsToWait / SECONDS.toMicros(1L);
}
2、reserve(permits)
final long reserve(int 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);
}
3、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;
4、storedPermitsToWaitTime( )
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)
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);
//计算阈值之上许可需要消耗的时间,除以2是当前预热期内生产一个许可的平均时间
micros = (long) (permitsAboveThresholdToTake * length / 2.0);
//重写计算permitsToTake ,这些许可来源于阈值之下的许可
permitsToTake -= permitsAboveThresholdToTake;
}
// measuring the integral on the left part of the function (the horizontal line)
//获取到申请许可的总时间
micros += (long) (stableIntervalMicros * permitsToTake);
return micros;
}
总结
Guava的预热算法,会间隔性的发放令牌,间隔时间根据预热时间和稳定时间一起确定。