Guava RateLimiter SmoothWarmingUp 允许突发流量的预热限流器


前言


一、预热原理

在这里插入图片描述

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的预热算法,会间隔性的发放令牌,间隔时间根据预热时间和稳定时间一起确定。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_lrs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值