RateLimiter 原理

简介
RateLimiter 是 Google 开源工具包提供的限流工具,该组件基于令牌桶实现限流算法
RateLimiter 有两种模式,一种是 SmoothBursty,另外一种则是 SmoothWarmingUp
SmoothBursty模式的令牌生成速度恒定,而SmoothWarmingUp:令牌生成速度缓慢提升直到维持在一个稳定值
本文着重介绍更为常见的 SmoothBursty模式

SmoothBursty的一些核心变量

  /**
   * 已生成的令牌
   */
  double storedPermits;

  /**
   * 可储存的最大令牌
   */
  double maxPermits;

  /**
   * 令牌生成的最大时间
   */
  double stableIntervalMicros;

  /**
   * 下一次能获取到令牌的时间
   */
  private long nextFreeTicketMicros = 0L; // could be either in the past or future

acquire(int permits)

  public double acquire(int permits) {
    long microsToWait = reserve(permits);
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
  }

a.获取一个等待时间microsToWait
b.sleep一段时间microsToWait
c.返回等待时间

reserve(int permits)

  final long reserve(int permits) {
    checkPermits(permits);
    synchronized (mutex()) {
      return reserveAndGetWaitLength(permits, stopwatch.readMicros());
    }
  }

此处添加了 synchronized,以保证多线程环境下获取令牌的线程安全

reserveAndGetWaitLength(int permits, long nowMicros)

  final long reserveAndGetWaitLength(int permits, long nowMicros) {
    long momentAvailable = reserveEarliestAvailable(permits, nowMicros);
    return max(momentAvailable - nowMicros, 0);
  }

momentAvailable 这个单词蛮形象的,可获得的时刻,也就是说,当前线程要到达 momentAvailable 这个时刻才能拿到令牌

resync(long nowMicros)

  void resync(long nowMicros) {
    if (nowMicros > nextFreeTicketMicros) {
      double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();
      storedPermits = min(maxPermits, storedPermits + newPermits);
      nextFreeTicketMicros = nowMicros;
    }
  }

更新令牌数量,具体规则如下:
(当前时间 - 记录的下一次获取令牌时间) / 令牌生成的间隔时间,这里算出来的就是这段时间理论上生成了多少令牌
然后min(最大令牌数, 存储令牌数 + 生产令牌数),得到了当前的存储令牌数
最后更新一下nextFreeTicketMicros

思考:按照比较简单的想法,去实现令牌桶算法,很容易想到的是单独使用一个定时器去定时往桶里放令牌,但是 RateLimiter显然没有这样做,而是通过 synchronized 去进行线程安全的保证,同时将更新令牌同的操作交给申请的线程来进行,那这样有什么好处?个人觉得有三点如下:
a.编程简单,并发安全问题直接使用synchronized就可以解决
b.无需额外的线程开销,毕竟同一个项目里面可能多处用到 RateLimiter
c.基于与分配的策略可以实现等待令牌的功能。例子:现在有1个令牌,但是我想一次性申请3个,那么我只需要等待两个令牌的时间就行了,这也就是acquire(int permits) 去计算microsToWait的意义了

  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;
  }

a.更新令牌数
b.计算 waitMicros ,然后 nextFreeTicketMicros = nextFreeTicketMicros + waitMicros
其实从这里就可以发现,这并不是严格意义上的令牌桶算法了,因为她允许提前使用未生成的令牌,
并且影响下一次获取令牌的时间

如果难以理解,可以运行一下下面代码,相信有助于理解

    public static void main(String[] args) {
        RateLimiter rateLimiter = RateLimiter.create(1);
        double acquire = rateLimiter.acquire(9999999);
        System.out.println(acquire);
    }

总结
RateLimiter 是基于令牌桶算法实现的限流器,但是严格来说并不是传统意义上的令牌桶,因为它允许提前消费令牌,并且会因此影响到下一次获取令牌的时间,在多线程方面 ,RateLimiter 使用 synchronized 来保证线程安全的问题,同时将更新令牌数量这个操作交给申请令牌的线程处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值