常见限流实现方式之RateLimiter实现限流

一 RateLimiter介绍

RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单高效的完成限流。

所以需要引入guava包

<dependency>
     <groupId>com.google.guava</groupId>
     <artifactId>guava</artifactId>
     <version>25.1-jre</version>
 </dependency>

guava的RateLimiter使用的是令牌桶算法,也就是以固定的频率向桶中放入令牌,例如一秒钟10枚令牌,实际业务在每次响应请求之前都从桶中获取令牌,只有取到令牌的请求才会被成功响应,获取的方式有两种:阻塞等待令牌或者取不到立即返回失败

二 使用举例:

/** 
 * Created by wuwf on 17/7/11. 
 * 有很多个任务,但希望每秒不超过X个,可用此类 
 */  
public class Demo1 {  
  
    public static void main(String[] args) {  
        //0.5代表一秒最多多少个  
        RateLimiter rateLimiter = RateLimiter.create(0.5);  
        List<Runnable> tasks = new ArrayList<Runnable>();  
        for (int i = 0; i < 10; i++) {  
            tasks.add(new UserRequest(i));  
        }  
        ExecutorService threadPool = Executors.newCachedThreadPool();  
        for (Runnable runnable : tasks) {  
            System.out.println("等待时间:" + rateLimiter.acquire());  
            threadPool.execute(runnable);  
        }  
    }  
  
    private static class UserRequest implements Runnable {  
        private int id;  
  
        public UserRequest(int id) {  
            this.id = id;  
        }  
  
        public void run() {  
            System.out.println(id);  
        }  
    }  
  
}  

  

该例子是多个线程依次执行,限制每2秒最多执行一个。运行看结果

我们限制了2秒放行一个,可以看到第一个是直接执行了,后面的每2秒会放行一个。
 
rateLimiter.acquire()该方法会阻塞线程,直到令牌桶中能取到令牌为止才继续向下执行,并返回等待的时间。

三 常见限流实现方式

目前常见的共四种方式

1通过限制单位时间段内调用量来限流

通过限制某个服务的单位时间内的调用量来进行限流。我们需要做的就是通过一个计数器统计单位时间段某个服务的访问量,如果超过了我们设定的阈值,
则该单位时间段内则不允许继续访问,或者把接下来的请求放入队列中等待到下一个单位时间段继续访问。

  • 优点:实现简单,阈值可动态配置。
  • 缺点:若单位时间内前一小段时间内就被大流量消耗完,则将导致该时间段内剩余的时间都拒绝服务。该现象为:“突刺消耗”。

2通过限制系统的并发调用程度来限流

通过并发限制来限流,我们通过严格限制某服务的并发访问程度,其实也就限制了该服务单位时间段内的访问量,
比如限制服务的并发访问数是100,而服务处理的平均耗时是10毫秒,该服务每秒能提供( 1000 / 10 ) * 100 = 10,000 次。

  • 优点:有更严格的限制边界,适合连接数、线程数的一个限制。
  • 缺点:对服务来说,并发阈值调优困难,难以准确判定服务阈值设置多少合适。一般采用Semaphore实现,但Semaphore没有提供重设信号量的方法,所以阈值动态配置也是问题。

3漏桶算法

请求流量以不确定速率申请资源,程序处理以恒定的速率进行,就是漏桶算法的基本原理。

漏斗有一个进水口 和 一个出水口,出水口以一定速率出水,并且有一个最大出水速率:

  • 在漏斗中没有水的时候
    • 如果进水速率小于等于最大出水速率,那么,出水速率等于进水速率,此时,不会积水
    • 如果进水速率大于最大出水速率,那么,漏斗以最大速率出水,此时,多余的水会积在漏斗中
  • 在漏斗中有水的时候
    • 出水口以最大速率出水
    • 如果漏斗未满,且有进水的话,那么这些水会积在漏斗中

如果漏斗已满,且有进水的话,那么这些水会溢出到漏斗之外。

  • 优点:不管突然流量有多大,漏桶都保证了流量的常速率输出。
  • 缺点:漏桶的出水速度是恒定的,那么意味着如果瞬时大流量的话,将有大部分请求被丢弃掉。

4令牌桶算法

对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。

令牌桶算法的原理是系统以恒定的速率产生令牌,然后把令牌放到令牌桶中,令牌桶有一个容量,当令牌桶满了的时候,再向其中放令牌,
那么多余的令牌会被丢弃;当想要处理一个请求的时候,需要从令牌桶中取出一个令牌,如果此时令牌桶中没有令牌,那么则拒绝该请求。

  • 优点:令牌桶算法能够在限制调用的平均速率的同时还允许某种程度的突发调用。guava RateLimiter就是基于令牌桶算法实现,所以代码实现简易。可动态配置令牌生成速率。
  • 缺点:

基于以上四种算法的介绍,令牌桶不仅能够限制调用的平均速率同时还允许一定程度的突发调用,不会导致突发调用大量请求被丢弃,更加灵活,且代码实现简易。综上:建议选择令牌桶算法实现限流。

猜你喜欢

转载自www.cnblogs.com/wanghongsen/p/11928608.html