Guava RateLimiter的实现

限流

高并发系统有三大利器:缓存 、限流 、降级。
对于限流的实现,有多种算法:计数器,漏桶法,令牌桶法。
计数器法无法应对极短时间内的过量请求,而漏桶法无法处理短时间内的突发请求,而令牌桶法能够解决上述两个问题。所以,一般常用的是令牌桶法。
这里的限流与 Spring Cloud 的 Hystrix 的限流差异比较大,前者是“保护下游”,后者是为了“保护自我”。并且,RateLimiter 关心的其实是“流量整形”,将不规整流量在一定速度内规整。

Guava RateLimiter

Guava类库中提供了令牌痛限流器的工具类RateLimiter,下面,我们具体看下Guava中令牌桶的具体实现。

RateLimiter的使用:

  1. 首先通过RateLimiter.create(1);创建一个限流器,参数代表每秒生成的令牌数
  2. 通过limiter.acquire(i);来以阻塞的方式获取令牌,
    也可以通过tryAcquire(int permits, long timeout, TimeUnit unit)来设置等待超时时间的方式获取令牌,如果超timeout为0,则代表非阻塞,获取不到立即返回。

RateLimiter的实现

如何持续产生令牌?
接近现实生活的想法是,由一个单独的线程定时向桶中放入token(其实就是对一个计数进行增加),但这样做会无谓的浪费处理器资源。
另一种解法则是延迟计算,它是在尝试获取令牌时,根据上次产生空闲令牌的时间点和令牌产生的速率来计算等待时间以及下次产生新的空闲令牌的时间点。
具体的代码如下,主要逻辑在reserveNextTicket中:

 public void acquire(int permits) {
    checkPermits(permits);
    long microsToWait;
    synchronized (mutex) {
        microsToWait = reserveNextTicket(permits, readSafeMicros());
    }
    ticker.sleepMicrosUninterruptibly(microsToWait);
  }
  
    /**
     * Reserves next ticket and returns the wait time that the caller must wait             for.
     */
    private long reserveNextTicket(double requiredPermits, long nowMicros) {
        resync(nowMicros);
        long microsToNextFreeTicket = nextFreeTicketMicros - nowMicros;
        double storedPermitsToSpend = Math.min(requiredPermits,                                     this.storedPermits);
        double freshPermits = requiredPermits - storedPermitsToSpend;

        long waitMicros = storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
    + (long) (freshPermits * stableIntervalMicros);

        this.nextFreeTicketMicros = nextFreeTicketMicros + waitMicros;
        this.storedPermits -= storedPermitsToSpend;
        return microsToNextFreeTicket;
  }

具体的讲,每次aquire都会调用resync计算桶中的令牌数,每次aquire后都会计算下一个可用的token的产生时刻nextFreeTicketMicros,aquire时,会将nextFreeTicketMicros减去当前时刻,得到的就是等待时间,同时会更新存储的token数目,以及nextFreeTicketMicros。

如上,是获取令牌的主要逻辑。

两种RateLimiter

Guava中的RateLimiter有两种实现:

  • WarmingUp(令牌生成速度缓慢提升到稳定值)
  • Bursty(令牌生成速度恒定)

当服务一段时间没有被使用时,RateLimiter会积累一定数目的令牌,从服务的角度来看,这时有两种情况:
(1)服务有大量的空闲资源,可供调用者使用;—— BurstyRateLimiter
(2)由于长时间没有使用,缓存等功能失效,启动时需要预热操作;—— WarmingUpRateLimiter

预热版本WarmingUpRateLimiter的特别之处在于
(1)桶中令牌数目的算法
(2)下次新的可用令牌产生的时间的算法:
如下图


image.png

你可能感兴趣的:(Guava RateLimiter的实现)