guava RateLimiter源码解读

本文详细解析了令牌桶算法的工作原理,重点介绍了如何通过维护下次可获取令牌时间和已积累的令牌数来控制流量速率,以及在不同模式下(如smoothbursty和smoothwarmup)的实现差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 @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;
      }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值