Guava RateLimiter源码理解

SmoothBursty#

create()创建限流器#

重要参数#

  • nextFreeTicketMicros 预计下次发放令牌的时间
  • stableIntervalMicros 两次发放令牌之间的时间间隔
  • maxPermits 最大能存储的令牌的数量
  • storedPermits 已经存储的令牌数
  public static RateLimiter create(double permitsPerSecond) {
    /*
     * The default RateLimiter configuration can save the unused permits of up to one second. This
     * is to avoid unnecessary stalls in situations like this: A RateLimiter of 1qps, and 4 threads,
     * all calling acquire() at these moments:
     *
     * T0 at 0 seconds
     * T1 at 1.05 seconds
     * T2 at 2 seconds
     * T3 at 3 seconds
     *
     * Due to the slight delay of T1, T2 would have to sleep till 2.05 seconds, and T3 would also
     * have to sleep till 3.05 seconds.
     */
    return create(permitsPerSecond, SleepingStopwatch.createFromSystemTimer());
  }
  
  static RateLimiter create(double permitsPerSecond, SleepingStopwatch stopwatch) {
    RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);
    rateLimiter.setRate(permitsPerSecond);
    return rateLimiter;
  }
  
  public final void setRate(double permitsPerSecond) {
    checkArgument(
        permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive");
        //同步设置
    synchronized (mutex()) {
      doSetRate(permitsPerSecond, stopwatch.readMicros());
    }
  }

SleepingStopwatch.createFromSystemTimer()
此处创建一个秒表StopWatch:

  • readMicros() 读取从秒表开始创建起到当前的纳秒时间

  • sleepMicrosUninterruptibly() 阻塞等待, 等待生产出本次requiredPermits数量所需要的时长, 如果之前还有等待的那就等待更长时间

    public static final SleepingStopwatch createFromSystemTimer() {
      return new SleepingStopwatch() {
        //初始化秒表的状态
        final Stopwatch stopwatch = Stopwatch.createStarted();
        
        //读取从秒表开始创建起到当前流逝的时间, 相对值
        @Override
        protected long readMicros() {
          return stopwatch.elapsed(MICROSECONDS);
        }
        //阻塞等待
        @Override
        protected void sleepMicrosUninterruptibly(long micros) {
          if (micros > 0) {
            Uninterruptibles.sleepUninterruptibly(micros, MICROSECONDS);
          }
        }
      };
    }

createStarted()初始化秒表的状态

  public static Stopwatch createStarted() {
    return new Stopwatch().start();
  }
  public Stopwatch start() {
    checkState(!isRunning, "This stopwatch is already running.");
    //秒表
    isRunning = true;
    startTick = ticker.read();
    return this;
  }

doSetRate()初始化的核心流程

    final void doSetRate(double permitsPerSecond, long nowMicros) {
    //同步
    resync(nowMicros);
    //每个令牌生成的时间间隔
    double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;
    this.stableIntervalMicros = stableIntervalMicros;
    //
    doSetRate(permitsPerSecond, stableIntervalMicros);
    }
  1. resync()同步,刷新为最新的状态
  //实际上是一种延迟同步, 下一次请求到来的时候重新算出生成的新令牌数,并且重置当前时间令牌生成起始时间
  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;
    }
  }
  1. doSetRate()时间间隔
    @Override
    void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
      double oldMaxPermits = this.maxPermits;
      maxPermits = maxBurstSeconds * permitsPerSecond;
      if (oldMaxPermits == Double.POSITIVE_INFINITY) {
        storedPermits = maxPermits;
      } else {
        storedPermits =
            (oldMaxPermits == 0.0)
                ? 0.0 // initial state
                //当动态设置QPS即permitsPerSecond, 则按比例设置storedPermits
                : storedPermits * maxPermits / oldMaxPermits;
      }
    }

acquire() 阻塞请求#

  @CanIgnoreReturnValue
  public double acquire(int permits) {
    //获取令牌
    long microsToWait = reserve(permits);
    //阻塞等待
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
  }
  
  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);
    //momentAvailable就是resync之后的nextFreeTicketMicros,如果nowMicros > nextFreeTicketMicros, 最后nowMicros == nextFreeTicketMicros, 那么就是不需要sleep的, 因为此时已经认为等了足够的时间,已经认为产生了足够的ticket可以使用, 所以可以放行。
    return max(momentAvailable - nowMicros, 0);
  }

reserveEarliestAvailable()预订最快可用的令牌

  @Override
  final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
    //同上,同步
    resync(nowMicros);
    long returnValue = nextFreeTicketMicros;
    //当前请求令牌数requiredPermits
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    //冗余请求令牌数freshPermits, 是小数
    double freshPermits = requiredPermits - storedPermitsToSpend;
    //storedPermitsToWaitTime对于SmoothBursty为0,非预热型
    long waitMicros = storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend) 
    //冗余请求令牌数需等待的总时间,如果没有冗余请求此处nextFreeTicketMicros也不会变化, 补充足以本次消费的令牌还需要等待的时间
    + (long) (freshPermits * stableIntervalMicros);
    //重置令牌生成的起始时间为当前时间
    this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
    this.storedPermits -= storedPermitsToSpend;
    return returnValue;
  }

tryAcquire() 非阻塞请求#

  //timeout期望等待的时间是否可以取到令牌
  public boolean tryAcquire(int permits, long timeout, TimeUnit unit) {
  //
   long timeoutMicros = max(unit.toMicros(timeout), 0);
   checkPermits(permits);
   long microsToWait;
   synchronized (mutex()) {
     long nowMicros = stopwatch.readMicros();
     //判断是否能取到令牌
     if (!canAcquire(nowMicros, timeoutMicros)) {
       return false;
     } else {
       //走acquire()流程
       microsToWait = reserveAndGetWaitLength(permits, nowMicros);
     }
   }
   stopwatch.sleepMicrosUninterruptibly(microsToWait);
   return true;
 }
 
 private boolean canAcquire(long nowMicros, long timeoutMicros) {
   return queryEarliestAvailable(nowMicros) - timeoutMicros <= nowMicros;
 }
posted @   FynnWang  阅读(102)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示