RateLimiter 代码解析

355 阅读2分钟

rateLimiter是一种平滑突发限流器,桶的容量没有限制,在桶中存有令牌的时候允许突发的流量激增时刻。

看代码之前需要记住几个变量含义:

  • storedPermits 桶中的令牌数
  • intervalMicros 生成令牌的时间间隔
  • nextFreeTicketMicros 下一次请求可以获取令牌的起始时间. 由于RateLimiter允许预消费,上次请求预消费令牌后,下次请求需要等待相应的时间到nextFreeTicketMicros时刻才可以获取令牌.

如初始值nextFreeTicketMicros=0,intervalMicros=5, 如acquire(2) 则会更新到nextFreeTicketMicros+=intervalMicros*acquirePermits == 10

以下根据代码执行路径逐层分析

acuqire 获取令牌

  //获取令牌,指定获取令牌数量permits 
  public double acquire(int permits) {
    //计算获取令牌需要多长时间
    long microsToWait = reserve(permits);
    //睡眠相应的时间后返回表示获取令牌结束
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
  }

reserve 计算获取令牌需要多长时间

  final long reserve(int permits) {
    //校验permits>0
    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);
  }
  
  //真正计算时间的逻辑:
  //requiredPermits:需要的令牌数
  //nowMicros:当前时间
  final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
    //重新计算当前已生成的令牌数
    resync(nowMicros);
    //设置下一次请求可以获取令牌的起始时间为返回值,表示用户需要等待多久
    long returnValue = nextFreeTicketMicros;
    //计算可直接获得的令牌数,如storedPermits=1,requiredPermits=3, 则storedPermitsToSpend=1
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    //计算需要预支的令牌数,则例子中 freshPermits=2
    double freshPermits = requiredPermits - storedPermitsToSpend;
    //计算预支的令牌数需要等待多久
    long waitMicros =
        storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
            + (long) (freshPermits * stableIntervalMicros);
    //更新下次令牌获取时间为增加了令牌预支时间的,让下次获取令牌的线程去承担这次令牌预支的后果
    this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
    //更新已经生成的令牌数
    this.storedPermits -= storedPermitsToSpend;
    
    return returnValue;
  }

reserveEarliestAvailable 的两种情况:

  1. nextFreeTicketMicro>now() 表示上次获取令牌有预支行为,本次需要为上次的预支行为买单,等待上次的预支的时间,然后本次获取令牌依旧直接预支,让下次获取令牌为本次买单;
  2. nextFreeTicketMicro<=now() 表示桶中容有令牌,可直接获取,如容量不够则预支requiredPermits-storedPermits,并设置nextFreeTicketMicro加上预支的时间,让下次获取令牌为本次买单。

resync 重新计算令牌数

  void resync(long nowMicros) {
    //如果当前时间大于nextFreeTicketMicros时,重新计算桶中的令牌数
    if (nowMicros > nextFreeTicketMicros) {
      //当前时间和nextFreeTicketMicros的时间间隔 除以 生成令牌的时间间隔 
      double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();
      storedPermits = min(maxPermits, storedPermits + newPermits);
      nextFreeTicketMicros = nowMicros;
    }
  }