这篇“java如何实现单机限流”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“java如何实现单机限流”文章吧。
比如你希望自己的应用程序 QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里 扔1000个令牌,RateLimiter经常用于限制对一些物理资源或者逻辑资源的访 问速率。
对于单机版的限流,可以使用Google 开源的 Guava项目,这个项目提供了Google在Java项目中使用一些核心库,包含集合(Collections),缓存(Caching),并发编程库(Concurrency),常用注解(Common annotations),String操作,I/O操作方面的众多非常实用的函数。
这个项目也包含了限流的功能,其原理是根据令牌桶算法来实现。
提供了两种限流策略:
● 平滑突发限流(SmoothBursty)
● 平滑预热限流(SmoothWarmingUp)实现。
依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
方法描述:
场景一:
当我们希望某一个接口每秒的访问量不超过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<String, RateLimiter> 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<UserRequest> tasks = new ArrayList<UserRequest>();
// 准备工作,先初始化 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<String, RateLimiter> 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<UserRequest> tasks = new ArrayList<UserRequest>();
// 准备工作,先初始化 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;}
}
}
以上就是关于“java如何实现单机限流”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注亿速云行业资讯频道。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。