限流是通过对并发请求进行限速,继而达到保护高并发系统的目的。常见限流算法有漏桶算法及令牌桶算法。其流程可参考下图。

        漏桶算法思路比较简单,就是将请求放入桶中,然后以固定速率处理桶中请求,当桶满后会丢弃新来的请求。漏桶算法一般依靠有界队列实现。

        令牌桶算法则是先创建一个固定大小的令牌桶,按照固定速率向桶中加入令牌,每个请求要先获取到令牌才能进行处理。 没获取到令牌的请求则放入缓冲区等待或直接丢弃。

        guava限流器RateLimiter是通过令牌桶实现的经典限流器。其使用也比较简单,使用代码如下。

首先引入guava jar
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>24.1-jre</version>
		</dependency>

使用限流器
 // 创建每秒处理两个请求的令牌桶
  static RateLimiter limiter = RateLimiter.create(2);

  public static void main(String[] args) {
    for (int i = 0; i < 8; i++) {
      new Thread("thread-" + i) {
        @Override
        public void run() {
          System.out.println(Thread.currentThread().getName() + new Date());
          // 限流器限流
          limiter.acquire();
          System.out.println("限流后执行时间" + new Date());
        }
      }.start();
    }
  }

RateLimiter实现

create方法创建

@VisibleForTesting
  static RateLimiter create(double permitsPerSecond, SleepingStopwatch stopwatch) {
    // 创建实例,设置保存令牌的最大时间,默认1秒
    RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);
    // 设置令牌产生速率
    rateLimiter.setRate(permitsPerSecond);
    return rateLimiter;
  }


进入setRate

@Override
  final void doSetRate(double permitsPerSecond, long nowMicros) {
    //这里先通过调用resync生成令牌以及更新下一期令牌生成时间,调用时也会用到该方法,使用延迟调用的方式生成令牌,避免定时任务生成令牌。
    resync(nowMicros);
    double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;
    this.stableIntervalMicros = stableIntervalMicros;
    doSetRate(permitsPerSecond, stableIntervalMicros);
  }

  acquire申请令牌

@CanIgnoreReturnValue
  public double acquire(int permits) {
    // 得到可以获取令牌的时间
    long microsToWait = reserve(permits);
    // sllep等待
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
  }


进入reserve
  final long reserve(int permits) {
    checkPermits(permits);
    // 加锁进行令牌生成,最终调用SmoothRateLimiter.reserveEarliestAvailable方法
    synchronized (mutex()) {
      return reserveAndGetWaitLength(permits, stopwatch.readMicros());
    }
  }

@Override
  final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
    // 调用resync方法生成令牌
    resync(nowMicros);
    long returnValue = nextFreeTicketMicros;
    // 计算实际能提供的令牌数storedPermitsToSpend
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    // 计算需要新产生的令牌数freshPermits
    double freshPermits = requiredPermits - storedPermitsToSpend;
    // 根据freshPermits计算新产生这批令牌需要多长时间
    long waitMicros =
        storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
            + (long) (freshPermits * stableIntervalMicros);
    // 更新下一次令牌可用时间戳与当前桶中令牌数量
    this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
    this.storedPermits -= storedPermitsToSpend;
    return returnValue;
  }

更多推荐