Guava RateLimiter限流器使用示例

Stella981
• 阅读 809

Guava中的RateLimiter可以限制单进程中某个方法的速率,本文主要介绍如何使用,实现原理请参考文档:推荐:超详细的Guava RateLimiter限流原理解析推荐:RateLimiter 源码分析(Guava 和 Sentinel 实现)

1 基于spring-mvc的controller测试限流

完整代码可参考:https://github.com/sxpujs/spring-cloud-examples/tree/master/rest-service

1.1 增加Maven依赖:

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

1.2 AccessLimitService 限流Service类

@Service
public class AccessLimitService {

    // 每秒发出5个令牌
    RateLimiter rateLimiter = RateLimiter.create(5.0);

    /**
     * 尝试获取令牌
     */
    public boolean tryAcquire() {
        return rateLimiter.tryAcquire();
    }
}

1.3 控制器类

@RestController
@Slf4j
public class HelloController {

    @Autowired
    private AccessLimitService accessLimitService;

    @RequestMapping("/access")
    public String access() {
        if (accessLimitService.tryAcquire()) {
            log.info("start");
            // 模拟业务执行500毫秒
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "access success [" + LocalDateTime.now() + "]";
        } else {
            //log.warn("限流");
            return "access limit [" + LocalDateTime.now() + "]";
        }
    }
}

1.4 使用wrk工具模拟客户端发起多个请求

我们使用HTTP基准工具wrk来生成大量HTTP请求。在终端输入如下命令来测试:

wrk -t1 -c10 -d2s http://127.0.0.1:8080/access

服务端日志如下所示(稍做简化),可以看出前6行的执行时间是一样的,这是因为RateLimiter的默认实现SmoothBursty会缓存1秒的许可,在定义RateLimiter实例时,每秒5个许可,加上新占用的1个许可,一共有6个。从第7行开始,每0.2秒执行1次,符合预期。

2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-2] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-3] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-7] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-8] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-9] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-4] HelloController : start
2020-07-05 15:46:16.804  INFO --- [nio-8080-exec-1] HelloController : start
2020-07-05 15:46:17.005  INFO --- [io-8080-exec-11] HelloController : start
2020-07-05 15:46:17.204  INFO --- [nio-8080-exec-9] HelloController : start
2020-07-05 15:46:17.404  INFO --- [nio-8080-exec-5] HelloController : start
2020-07-05 15:46:17.604  INFO --- [nio-8080-exec-8] HelloController : start
2020-07-05 15:46:17.804  INFO --- [nio-8080-exec-2] HelloController : start
2020-07-05 15:46:18.004  INFO --- [nio-8080-exec-7] HelloController : start
2020-07-05 15:46:18.204  INFO --- [nio-8080-exec-6] HelloController : start
2020-07-05 15:46:18.404  INFO --- [nio-8080-exec-5] HelloController : start

2 基于单个类的main方法测试限流

package com.demo.guava;

import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

@Slf4j
public class RateLimiterDemo {

    static void submitTasks1() {
        ExecutorService pool = Executors.newFixedThreadPool(10);
        RateLimiter rateLimiter = RateLimiter.create(5); // rate is "5 permits per second"
        IntStream.range(0, 10).forEach(i -> pool.submit(() -> {
            if (rateLimiter.tryAcquire()) {
                try {
                    log.info("start");
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            } else {
                log.warn("限流");
            }
        }));
        pool.shutdown();
        /*
16:18:18.784 [pool-1-thread-1] INFO  RateLimiterDemo - start
16:18:18.784 [pool-1-thread-7] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-2] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-4] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-5] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-6] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-9] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-3] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-10] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-8] WARN  RateLimiterDemo - 限流
         */
    }

    static void submitTasks2() {
        ExecutorService pool = Executors.newFixedThreadPool(10);
        RateLimiter rateLimiter = RateLimiter.create(5); // rate is "5 permits per second"
        IntStream.range(0, 10).forEach(i -> pool.submit(() -> {
            rateLimiter.acquire();
            log.info("start");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));
        pool.shutdown();
        /*
16:18:56.030 [pool-1-thread-1] INFO  RateLimiterDemo - start
16:18:56.227 [pool-1-thread-10] INFO  RateLimiterDemo - start
16:18:56.428 [pool-1-thread-9] INFO  RateLimiterDemo - start
16:18:56.627 [pool-1-thread-8] INFO  RateLimiterDemo - start
16:18:56.827 [pool-1-thread-7] INFO  RateLimiterDemo - start
16:18:57.028 [pool-1-thread-6] INFO  RateLimiterDemo - start
16:18:57.226 [pool-1-thread-5] INFO  RateLimiterDemo - start
16:18:57.426 [pool-1-thread-4] INFO  RateLimiterDemo - start
16:18:57.629 [pool-1-thread-3] INFO  RateLimiterDemo - start
16:18:57.826 [pool-1-thread-2] INFO  RateLimiterDemo - start
         */
    }

    static void submitTasks3() {
        RateLimiter r = RateLimiter.create(5);
        log.info("start");
        for (;;) {
            log.info("get 1 tokens: " + r.acquire() + "s");
        }
        /*
16:15:46.310 [main] INFO RateLimiterDemo - start
16:15:46.315 [main] INFO RateLimiterDemo - get 1 tokens: 0.0s
16:15:46.513 [main] INFO RateLimiterDemo - get 1 tokens: 0.193752s
16:15:46.709 [main] INFO RateLimiterDemo - get 1 tokens: 0.194875s
16:15:46.911 [main] INFO RateLimiterDemo - get 1 tokens: 0.199033s
16:15:47.113 [main] INFO RateLimiterDemo - get 1 tokens: 0.197833s
16:15:47.312 [main] INFO RateLimiterDemo - get 1 tokens: 0.195898s
         */
    }

    static void submitTasks4() {
        RateLimiter r = RateLimiter.create(5);
        log.info("start");
        for (;;) {
            if (r.tryAcquire()) {
                log.info("run");
            }
        }
        /*
16:17:17.098 [main] INFO  RateLimiterDemo - start
16:17:17.100 [main] INFO  RateLimiterDemo - run
16:17:17.296 [main] INFO  RateLimiterDemo - run
16:17:17.496 [main] INFO  RateLimiterDemo - run
16:17:17.696 [main] INFO  RateLimiterDemo - run
         */
    }

    public static void main(String[] args) throws InterruptedException {
        //submitTasks1();
        submitTasks2();
        //submitTasks3();
        //submitTasks4();
    }
}

参考文档:

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Sentinel 是如何做限流的
限流是保障服务高可用的方式之一,尤其是在微服务架构中,对接口或资源进行限流可以有效地保障服务的可用性和稳定性。之前的项目中使用的限流措施主要是Guava的RateLimiter。RateLimiter是基于令牌桶流控算法,使用非常简单,但是功能相对比较少。而现在,我们有了一种新的选择,阿里提供的Sentinel。Sentinel是阿里巴巴提供
Stella981 Stella981
3年前
Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法
Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法参考文章:(1)Google地球出现“无法连接到登录服务器(错误代码:c00a0194)”解决方法(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.codeprj.com%2Fblo
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这