这篇文章主要为大家详细介绍了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实现单机限流
基础教程推荐
- Java文件管理操作的知识点整理 2023-05-19
- Java实现线程插队的示例代码 2022-09-03
- ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02
- java实现多人聊天系统 2023-05-19
- springboot自定义starter方法及注解实例 2023-03-31
- Java并发编程进阶之线程控制篇 2023-03-07
- Java实现查找文件和替换文件内容 2023-04-06
- Java数据结构之对象比较详解 2023-03-07
- java基础知识之FileInputStream流的使用 2023-08-11
- JDK数组阻塞队列源码深入分析总结 2023-04-18