java如何实现单机限流
作者:NorthPath
时间:2023-04-25
浏览:0
何时使用限流:比如你希望自己的应用程序QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里扔1000个令牌,RateLimiter经常用于限制对一些物理资源或者逻辑资源的访问速率。简介:对于单机版的限流,可以使用Google开源的Guava项目,这个项目提供了Google在Java项目中使用一些核心库,包含集合(Collections),缓存(Caching),并发编程库(Concurrency),常用注解(Commonannotations),String操作,I/O操
何时使用限流:
比如你希望自己的应用程序 QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里 扔1000个令牌,RateLimiter经常用于限制对一些物理资源或者逻辑资源的访 问速率。
简介:
对于单机版的限流,可以使用Google 开源的 Guava项目,这个项目提供了Google在Java项目中使用一些核心库,包含集合(Collections),缓存(Caching),并发编程库(Concurrency),常用注解(Common annotations),String操作,I/O操作方面的众多非常实用的函数。
这个项目也包含了限流的功能,其原理是根据令牌桶算法来实现。
提供了两种限流策略:
● 平滑突发限流(SmoothBursty)
● 平滑预热限流(SmoothWarmingUp)实现。

依赖:
com.google.guava guava 29.0-jre
方法描述:

模拟场景(示例):
场景一:
当我们希望某一个接口每秒的访问量不超过10次
package org.xhs.test;
import org.apache.curator.shaded.com.google.common.util.concurrent.RateLimiter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
/**
* @Author: hu.chen
* @Description:
**/
public class Test {
/**
* 存储接口名和令牌生成器的对应关系
*/
private static Map interfaces = new ConcurrentHashMap<>();
/**
* 线程池
*/
private static ExecutorService threadPool = new ThreadPoolExecutor(10,15,3,TimeUnit.SECONDS,new ArrayBlockingQueue<>(100));
public static void main(String[] args) throws InterruptedException {
List tasks = new ArrayList();
// 准备工作,先初始化 10个线程(用户),这10个用户同时访问一个接口
for (int i = 1; i <= 12; i++) {
String ip = "127.0.0." + i;
String userName="chenhu_"+i;
String interfaceName="user/find_";
tasks.add(new UserRequest(ip,userName,interfaceName));
}
// 先初始化好令牌生成器
for (UserRequest request : tasks) {
// 根据接口名限流
RateLimiter rateLimiter = interfaces.get(request.getInterfaceName());
if(rateLimiter==null){
// 创建一个令牌生成器,每秒产生10个令牌
synchronized (interfaces) {
if(rateLimiter==null) {
rateLimiter = RateLimiter.create(10);
// 将这个令牌生成器和具体的接口进行绑定
interfaces.put(request.getInterfaceName(),rateLimiter);
}
}
}
}
// 休眠一秒,让令牌生成器先生成令牌
Thread.sleep(1000);
for (UserRequest request : tasks) {
// 根据接口名限流
RateLimiter rateLimiter = interfaces.get(request.getInterfaceName());
// 获取令牌桶中一个令牌,如果获取不到,则等待 timeout 时间,如果还获取不到,则返回false,反之则返回true
// timeout设置为0,表示不等待
if(rateLimiter.tryAcquire(1,0,TimeUnit.SECONDS)){
// 得到令牌,处理请求
threadPool.execute(()->{
System.err.println("接口:"+request.getInterfaceName()+" 访问还未达到上限,"+request.getUserName()+"可以访问");
});
}else {
// 已经等待了10秒还获取不到令牌,进行其他业务处理
System.err.println("当前时间访问失败,"+request.getUserName()+"无法获取令牌");
}
}
}
private static class UserRequest {
/**
* 请求用户ip
*/
private String ip;
/**
* 用户名
*/
private String userName;
/**
* 请求的接口名
*/
private String interfaceName;
public UserRequest(String ip, String userName, String interfaceName) {
this.ip = ip;
this.userName = userName;
this.interfaceName = interfaceName;
}
public String getIp() {return ip;}
public String getUserName() { return userName;}
public String getInterfaceName() {return interfaceName;}
}
} 场景二:
当我们希望某一个用户或者ip,每秒的访问量不超过10
package org.xhs.test;
import org.apache.curator.shaded.com.google.common.util.concurrent.RateLimiter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
/**
* @Author: hu.chen
* @Description:
**/
public class Test {
/**
* 存储用户名和令牌生成器的对应关系
*/
private static Map interfaces = new ConcurrentHashMap<>();
/**
* 线程池
*/
private static ExecutorService threadPool = new ThreadPoolExecutor(10,15,3,TimeUnit.SECONDS,new ArrayBlockingQueue<>(100));
public static void main(String[] args) throws InterruptedException {
List tasks = new ArrayList();
// 准备工作,先初始化 10个线程(用户),这10个用户同时访问一个接口
for (int i = 1; i <= 12; i++) {
String ip = "127.0.0." + i;
String userName="chenhu_";
String interfaceName="user/find_"+i;
tasks.add(new UserRequest(ip,userName,interfaceName));
}
// 先初始化好令牌生成器
for (UserRequest request : tasks) {
// 根据接口名限流
RateLimiter rateLimiter = interfaces.get(request.getUserName());
if(rateLimiter==null){
// 创建一个令牌生成器,每秒产生5个令牌
synchronized (interfaces) {
if(rateLimiter==null) {
rateLimiter = RateLimiter.create(10);
// 将这个令牌生成器和具体的接口进行绑定
interfaces.put(request.getUserName(),rateLimiter);
}
}
}
}
// 休眠一秒,让令牌生成器先生成令牌
Thread.sleep(1000);
for (UserRequest request : tasks) {
// 根据接口名限流
RateLimiter rateLimiter = interfaces.get(request.getUserName());
// 获取令牌桶中一个令牌,如果获取不到,则等待 timeout 时间,如果还获取不到,则返回false,反之则返回true
// timeout设置为0,表示不等待
if(rateLimiter.tryAcquire(1,0,TimeUnit.SECONDS)){
// 得到令牌,处理请求
threadPool.execute(()->{
System.err.println("用户:"+request.getUserName()+" 当前时间访问次数还未达到上限,可以访问");
});
}else {
// 已经等待了10秒还获取不到令牌,进行其他业务处理
System.err.println("当前时间访问失败,"+request.getUserName()+"无法获取令牌");
}
}
}
private static class UserRequest {
/**
* 请求用户ip
*/
private String ip;
/**
* 用户名
*/
private String userName;
/**
* 请求的接口名
*/
private String interfaceName;
public UserRequest(String ip, String userName, String interfaceName) {
this.ip = ip;
this.userName = userName;
this.interfaceName = interfaceName;
}
public String getIp() {return ip;}
public String getUserName() { return userName;}
public String getInterfaceName() {return interfaceName;}
}
}
作者最新文章
贵州省住建厅与贝壳集团签署旅居战略合作:五大维度落地方案解析
2026-09-08 18:13
上海链家安住APP:业主主动卖房功能与成交数据解析
2026-09-08 18:11
如何批量将PPT转成PDF格式?PPT转PDF工具怎么选?
2026-09-04 16:03
PDF文件怎么压缩?3个小技巧帮你减小体积
2026-09-03 18:03
小批量试产总结报告:新产品量产导入评审实战指南
2026-09-02 19:48
上一篇:
mysql引擎间的区别有哪些
热门文章
更多
精品专题
更多
Mac软件
更多
WINDOWS
更多


































