令牌桶算法-高性能限流器Guava Rate

高性能限流器Guava RateLimiter

我们来看看 Guava RateLimiter 是如何解决高并发场景下的限流问题的。

限流怎么理解呢?(我们创建一个流速为2个请求/秒的限流器)
直观地看,2个请求/秒就是每秒最多允许两个请求通过限流器。
在Guava中,2个请求/秒==一个请求500毫秒

首先我们看看RateLimiter的使用

//限流器流速:2个请求/秒
RateLimiter limiter = 
  RateLimiter.create(2.0);
//执行任务的线程池
ExecutorService es = Executors
  .newFixedThreadPool(1);
//记录上一次执行时间
prev = System.nanoTime();
//测试执行20次
for (int i=0; i<20; i++){
  //限流器限流
  limiter.acquire();
  //提交任务异步执行
  es.execute(()->{
    long cur=System.nanoTime();
    //打印时间间隔:毫秒
    System.out.println(
      (cur-prev)/1000_000);
    prev = cur;
  });
}

输出结果:
...
500
499
499
500
499

经典限流算法:令牌桶算法

Guava采用的就是令牌桶算法,核心就是通过限流器,必须拿到令牌。
令牌桶算法详情描述:

  1. 令牌以固定的速率添加到令牌桶中,假设限流的速率是 r/ 秒,则令牌每 1/r 秒会添加一个;
  2. 假设令牌桶的容量是 b ,如果令牌桶已满,则新的令牌会被丢弃;
  3. 请求能够通过限流器的前提是令牌桶中有令牌。

如果让我用java实现,我可能会想到生产设-消费者模式,一个生产者线程定时向阻塞队列中添加令牌,而试图通过限流器的线程作为消费者,只有从阻塞队列中取到令牌,才能通过限流器。
但是在高并发场景下,定时器的精度误差会特别大,同时定时器会创建调度线程,对系统的影响较大。

Guava实现令牌桶算法关键是记录一下令牌的发放时间。
下面通过图解来详细描述一下。
假设令牌桶的容量为 b=1,限流速率 r = 1 个请求 / 秒,
下图当前下一令牌产生时间在第三秒,t1线程在第二秒产生。
在这里插入图片描述
t1线程占用第三秒的令牌,下一令牌+1秒,到第四秒。
在这里插入图片描述
此时在第2秒t2线程产生,t2占用4秒的下一令牌。在这里插入图片描述
然后下一令牌继续+1秒,到第5秒。在这里插入图片描述
前面都是讲解的线程请求在令牌前,下面说一下请求在令牌后面的情况,
在这里插入图片描述
当前下一令牌产生时间在5秒,第7秒t3线程产生,拿到5秒的令牌,
因为限流每秒产生一个,所以下一令牌产生时间为当前时间+1秒(间隔时间)=8秒在这里插入图片描述
以上就是令牌的桶的简单实例情况。

发布了34 篇原创文章 · 获赞 0 · 访问量 1089

猜你喜欢

转载自blog.csdn.net/qq_42634696/article/details/104655701