如何guava的RateLimiter使用

Posted 盖丽男

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何guava的RateLimiter使用相关的知识,希望对你有一定的参考价值。

为什么要限流

在调用一些第三方的接口时,他们会有一些调用频率的限制,比如每秒不能超过多少次,这种时候,就需要用到限流的工具。

定义

还是给一个定义出来:
在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流

  • 缓存 缓存的目的是提升系统访问速度和增大系统处理容量
  • 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解决后再打开
  • 限流 限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理

如何限流

傻瓜方案

因为我们需要限流的地方是对第三方调用的时候,我们是在循环里调用的第三方,所以,简单粗暴的Thread.sleep(500) 其实也可以用。

正式方案

Guava的RateLimiter算是比较方便的限流工具。
Guava RateLimiter基于令牌桶算法,我们只需要告诉RateLimiter系统限制的QPS是多少,那么RateLimiter将以这个速度往桶里面放入令牌,然后请求的时候,通过acquire()方法向RateLimiter获取许可(令牌)。

来个例子

public static void main(String[] args) 
        // qps设置为2,代表一秒钟只允许处理2个并发请求
        RateLimiter rateLimiter = RateLimiter.create(2);
        StopWatch sw=new StopWatch();
        sw.start();
        int nTasks = 10;
        for (int i = 0; i < nTasks; i++) 

            rateLimiter.acquire(1);
            sw.split();
            System.out.println("job id is "+i+"sw: "+sw.getSplitTime());
            //sw.shortSummary();
        
        sw.stop();
        System.out.println("finish,total time is "+sw.getTime());

    

输出是这样的:

job id is 0sw: 0
job id is 1sw: 497
job id is 2sw: 998
job id is 3sw: 1499
job id is 4sw: 2002
job id is 5sw: 2498
job id is 6sw: 2997
job id is 7sw: 3497
job id is 8sw: 3998
job id is 9sw: 4502
finish,total time is 4502

注意

有一点需要注意,RateLimiter的这个令牌是会累积的,也就是说,就算令牌不使用,也会积攒下来,当需要使用的时候,一股脑的给出去。

RateLimiter rateLimiter = RateLimiter.create(2);
//追一下代码
    public static RateLimiter create(double permitsPerSecond) 
        return create(permitsPerSecond, RateLimiter.SleepingStopwatch.createFromSystemTimer());
    

    @VisibleForTesting
    static RateLimiter create(double permitsPerSecond, RateLimiter.SleepingStopwatch stopwatch) 
        RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0D);
        rateLimiter.setRate(permitsPerSecond);
        return rateLimiter;
    

可以看到,默认情况下,最多累积1s钟的令牌。

测试一下,代码:

    @SneakyThrows
    public static void main(String[] args) 
        // qps设置为2,代表一秒钟只允许处理2个并发请求
        RateLimiter rateLimiter = RateLimiter.create(2);
        StopWatch sw=new StopWatch();
        sw.start();
        int nTasks = 10;
        Thread.sleep(3000);
        for (int i = 0; i < nTasks; i++) 

            rateLimiter.acquire(1);
            sw.split();
            System.out.println("job id is "+i+"sw: "+sw.getSplitTime());
            //sw.shortSummary();
        
        sw.stop();
        System.out.println("finish,total time is "+sw.getTime());

    

结果:

job id is 0sw: 3009
job id is 1sw: 3010
job id is 2sw: 3010
job id is 3sw: 3509
job id is 4sw: 4009
job id is 5sw: 4510
job id is 6sw: 5008
job id is 7sw: 5506
job id is 8sw: 6007
job id is 9sw: 6508
finish,total time is 6508

如果你想多累积几秒,那么创建的时候应该是:

        RateLimiter rateLimiter = RateLimiter.create(2);
        rateLimiter.setRate(3D);

高并发之API接口限流

以上是关于如何guava的RateLimiter使用的主要内容,如果未能解决你的问题,请参考以下文章

如何guava的RateLimiter使用

使用Guava RateLimiter限流

Guava RateLimiter详解以及源码分析

Day857.高性能限流器Guava RateLimiter -Java 并发编程实战

Day857.高性能限流器Guava RateLimiter -Java 并发编程实战

Guava官方文档-RateLimiter类