当前位置: 首页 > news >正文

Guava-RateLimiter详解

简介: 常用的限流算法有漏桶算法和令牌桶算法,guava的RateLimiter使用的是令牌桶算法,也就是以固定的频率向桶中放入令牌,例如一秒钟10枚令牌,实际业务在每次响应请求之前都从桶中获取令牌,只有取到令牌的请求才会被成功响应,获取的方式有两种:阻塞等待令牌或者取不到立即返回失败,下图来自网上: ratelimite原理图 本次实战,我们用的是guava的RateLimiter,场景是spring mvc在处理请求时候,从桶中申请令牌,申请到了就成功响应,申请不到时直接返回失败。

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

img_6066ccb709d7c047dbbd866dd40295e9.png

ratelimite原理图

本次实战,我们用的是guava的RateLimiter,场景是spring mvc在处理请求时候,从桶中申请令牌,申请到了就成功响应,申请不到时直接返回失败。

实例

1、添加guava jar包

    <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>18.0</version></dependency>

2、AccessLimitService.java 限流服务封装到一个类中AccessLimitService,提供tryAcquire()方法,用来尝试获取令牌,返回true表示获取到

@Service
public class AccessLimitService {//每秒只发出5个令牌RateLimiter rateLimiter = RateLimiter.create(5.0);/*** 尝试获取令牌* @return*/public boolean tryAcquire(){return rateLimiter.tryAcquire();}
}

3、Controller层每次收到请求的时候都尝试去获取令牌,获取成功和失败打印不同的信息

@Controller
public class HelloController {private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");@Autowiredprivate AccessLimitService accessLimitService;@RequestMapping("/access")@ResponseBodypublic String access(){//尝试获取令牌if(accessLimitService.tryAcquire()){//模拟业务执行500毫秒try {Thread.sleep(500);}catch (InterruptedException e){e.printStackTrace();}return "aceess success [" + sdf.format(new Date()) + "]";}else{return "aceess limit [" + sdf.format(new Date()) + "]";}}
}

4、测试:十个线程并发访问接口


public class AccessClient {ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);/*** get请求* @param realUrl* @return*/public static String sendGet(URL realUrl) {String result = "";BufferedReader in = null;try {// 打开和URL之间的连接URLConnection connection = realUrl.openConnection();// 设置通用的请求属性connection.setRequestProperty("accept", "*/*");connection.setRequestProperty("connection", "Keep-Alive");connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");// 建立实际的连接connection.connect();// 定义 BufferedReader输入流来读取URL的响应in = new BufferedReader(new InputStreamReader(connection.getInputStream()));String line;while ((line = in.readLine()) != null) {result += line;}} catch (Exception e) {System.out.println("发送GET请求出现异常!" + e);e.printStackTrace();}// 使用finally块来关闭输入流finally {try {if (in != null) {in.close();}} catch (Exception e2) {e2.printStackTrace();}}return result;}public void access() throws Exception{final URL url = new URL("http://localhost:8080/guavalimitdemo/access");for(int i=0;i<10;i++) {fixedThreadPool.submit(new Runnable() {public void run() {System.out.println(sendGet(url));}});}fixedThreadPool.shutdown();fixedThreadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);}public static void main(String[] args) throws Exception{AccessClient accessClient = new AccessClient();accessClient.access();}
}

部分请求由于获取的令牌可以成功执行,其余请求没有拿到令牌,我们可以根据实际业务来做区分处理。还有一点要注意,我们通过RateLimiter.create(5.0)配置的是每一秒5枚令牌,但是限流的时候发出的是6枚,改用其他值验证,也是实际的比配置的大1。

以上就是快速实现限流的实战过程,此处仅是单进程服务的限流,而实际的分布式服务中会考虑更多因素,会复杂很多。


RateLimiter方法摘要

修饰符和类型方法和描述
doubleacquire() 从RateLimiter获取一个许可,该方法会被阻塞直到获取到请求
doubleacquire(int permits)从RateLimiter获取指定许可数,该方法会被阻塞直到获取到请求
static RateLimitercreate(double permitsPerSecond)根据指定的稳定吞吐率创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少查询)
static RateLimitercreate(double permitsPerSecond, long warmupPeriod, TimeUnit unit)根据指定的稳定吞吐率和预热期来创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少个请求量),在这段预热时间内,RateLimiter每秒分配的许可数会平稳地增长直到预热期结束时达到其最大速率。(只要存在足够请求数来使其饱和)
doublegetRate()返回RateLimiter 配置中的稳定速率,该速率单位是每秒多少许可数
voidsetRate(double permitsPerSecond)更新RateLimite的稳定速率,参数permitsPerSecond 由构造RateLimiter的工厂方法提供。
StringtoString()返回对象的字符表现形式
booleantryAcquire()从RateLimiter 获取许可,如果该许可可以在无延迟下的情况下立即获取得到的话
booleantryAcquire(int permits)从RateLimiter 获取许可数,如果该许可数可以在无延迟下的情况下立即获取得到的话
booleantryAcquire(int permits, long timeout, TimeUnit unit)从RateLimiter 获取指定许可数如果该许可数可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可数的话,那么立即返回false (无需等待)
booleantryAcquire(long timeout, TimeUnit unit)从RateLimiter 获取许可如果该许可可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待)
  • 举例来说明如何使用RateLimiter,想象下我们需要处理一个任务列表,但我们不希望每秒的任务提交超过两个:
//速率是每秒两个许可
final RateLimiter rateLimiter = RateLimiter.create(2.0);
void submitTasks(List tasks, Executor executor) {for (Runnable task : tasks) {rateLimiter.acquire(); // 也许需要等待executor.execute(task);}
}

官方文档:http://ifeve.com/guava-ratelimiter

相关文章:

  • 2023-8-20 CVTE视源股份后端开发实习一面
  • LuatOS-SOC接口文档(air780E)--mcu - 封装mcu一些特殊操作
  • Springboot之拦截器Interceptor
  • ROS采用vector动态传递数组参数
  • 企业信息集成
  • H3C LC-5120-52SC-HI配置管理IP
  • 分布式日志和链路追踪
  • C++ 的int*p[]和int(*p)[]的区别
  • 【代码随想录】算法训练计划03
  • 8.MySQL内外连接
  • 交换机基础(四):MSTP负载均衡配置案例
  • vue+golang上传微信头像
  • Codeforces Round 905 (Div. 3)ABCDEF
  • python+unittest+requests+HTMLRunner编写接口自动化测试集
  • RDMA概览
  • [PHP内核探索]PHP中的哈希表
  • [case10]使用RSQL实现端到端的动态查询
  • 2017-09-12 前端日报
  • Angular 2 DI - IoC DI - 1
  • CSS 三角实现
  • Dubbo 整合 Pinpoint 做分布式服务请求跟踪
  • echarts花样作死的坑
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • isset在php5.6-和php7.0+的一些差异
  • javascript面向对象之创建对象
  • Java的Interrupt与线程中断
  • Java小白进阶笔记(3)-初级面向对象
  • learning koa2.x
  • Median of Two Sorted Arrays
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • rabbitmq延迟消息示例
  • Redis 懒删除(lazy free)简史
  • 蓝海存储开关机注意事项总结
  • 目录与文件属性:编写ls
  • 源码之下无秘密 ── 做最好的 Netty 源码分析教程
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • (3)STL算法之搜索
  • (8)STL算法之替换
  • (二)JAVA使用POI操作excel
  • (分享)自己整理的一些简单awk实用语句
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (一)认识微服务
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • ******之网络***——物理***
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
  • .NET 中创建支持集合初始化器的类型
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • .NET框架设计—常被忽视的C#设计技巧
  • .net连接oracle数据库
  • /dev/VolGroup00/LogVol00:unexpected inconsistency;run fsck manually
  • @param注解什么意思_9000字,通俗易懂的讲解下Java注解
  • []error LNK2001: unresolved external symbol _m
  • [ai笔记9] openAI Sora技术文档引用文献汇总
  • [Android] 修改设备访问权限
  • [Big Data - Kafka] kafka学习笔记:知识点整理