SpringBoot如何使用RateLimiter通过AOP方式进行限流
作者:yellow_han 发布时间:2023-09-16 18:18:44
使用RateLimiter通过AOP方式进行限流
1、引入依赖
<!-- guava 限流 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>25.1-jre</version>
</dependency>
2、自定义注解
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceLimit {
String description() default "";
}
3、AOP实现类
@Component
@Scope
@Aspect
public class LimitAspect {
每秒只发出5个令牌,此处是单进程服务的限流,内部采用令牌捅算法实现
private static RateLimiter rateLimiter = RateLimiter.create(5.0);
//Service层切点 限流
@Pointcut("@annotation(com.itstyle.seckill.common.aop.ServiceLimit)")
public void ServiceAspect() {
}
@Around("ServiceAspect()")
public Object around(ProceedingJoinPoint joinPoint) {
Boolean flag = rateLimiter.tryAcquire();
Object obj = null;
try {
if(flag){
obj = joinPoint.proceed();
}
} catch (Throwable e) {
e.printStackTrace();
}
return obj;
}
}
4、使用
@Override
@ServiceLimit
@Transactional
public Result startSeckil(long seckillId,long userId) {
//todo 操作
}
SpringBoot之限流
限流的基础算法
令牌桶和漏桶
漏桶算法 的实现往往依赖于队列,请求到达如果队列未满则直接放入队列,然后有一个处理器按照固定频率从队列头取出请求进行处理。如果请求量大,则会导致队列满,那么新来的请求就会被抛弃。
令牌桶算法 则是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌。桶中存放的令牌数有最大上限,超出之后就被丢弃或者拒绝。当流量或者网络请求到达时,每个请求都要获取一个令牌,如果能够获取到,则直接处理,并且令牌桶删除一个令牌。如果获取不到,该请求就要被限流,要么直接丢弃,要么在缓冲区等待。
令牌桶和漏桶对比
令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求;漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝;
令牌桶限制的是平均流入速率,允许突发请求,只要有令牌就可以处理,支持一次拿3个令牌,4个令牌;漏桶限制的是常量流出速率,即流出速率是一个固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2,从而平滑突发流入速率;
令牌桶允许一定程度的突发,而漏桶主要目的是平滑流出速率;
Guava RateLimiter
1.依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
<optional>true</optional>
</dependency>
2.示例代码
@Slf4j
@Configuration
public class RequestInterceptor implements HandlerInterceptor {
// 根据字符串分不同的令牌桶, 每天自动清理缓存
private static LoadingCache<String, RateLimiter> cachesRateLimiter = CacheBuilder.newBuilder()
.maximumSize(1000) //设置缓存个数
/**
* expireAfterWrite是在指定项在一定时间内没有创建/覆盖时,会移除该key,下次取的时候从loading中取
* expireAfterAccess是指定项在一定时间内没有读写,会移除该key,下次取的时候从loading中取
* refreshAfterWrite是在指定时间内没有被创建/覆盖,则指定时间过后,再次访问时,会去刷新该缓存,在新值没有到来之前,始终返回旧值
* 跟expire的区别是,指定时间过后,expire是remove该key,下次访问是同步去获取返回新值;
* 而refresh则是指定时间后,不会remove该key,下次访问会触发刷新,新值没有回来时返回旧值
*/
.expireAfterAccess(1, TimeUnit.HOURS)
.build(new CacheLoader<String, RateLimiter>() {
@Override
public RateLimiter load(String key) throws Exception {
// 新的字符串初始化 (限流每秒2个令牌响应)
return RateLimiter.create(2);
}
});
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.info("request请求地址path[{}] uri[{}]", request.getServletPath(), request.getRequestURI());
try {
String str = "hello";
// 令牌桶
RateLimiter rateLimiter = cachesRateLimiter.get(str);
if (!rateLimiter.tryAcquire()) {
System.out.println("too many requests.");
return false;
}
} catch (Exception e) {
// 解决 * 的异常,全局异常处理器捕获不到的问题
request.setAttribute("exception", e);
request.getRequestDispatcher("/error").forward(request, response);
}
return true;
}
}
3.测试
@RestController
@RequestMapping(value = "user")
public class UserController {
@GetMapping
public Result test2(){
System.out.println("1111");
return new Result(true,200,"");
}
}
http://localhost:8080/user/
如果没有result类,自己可以随便返回个字符串
4.测试结果
其他
创建
RateLimiter提供了两个工厂方法:
一个是平滑突发限流
RateLimiter r = RateLimiter.create(5); //项目启动,直接允许5个令牌
一个是平滑预热限流
RateLimiter r = RateLimiter.create(2, 3, TimeUnit.SECONDS); //项目启动后3秒后才会到达设置的2个令牌
缺点
RateLimiter只能用于单机的限流,如果想要集群限流,则需要引入redis或者阿里开源的sentinel中间件。
TimeUnit.SECONDS);` //项目启动后3秒后才会到达设置的2个令牌
来源:https://blog.csdn.net/u014769528/article/details/84526363


猜你喜欢
- 一、什么是Spring Cloud?SpringCloud 对常见的分布式系统模式提供了简单易用的编程模型,帮助开发者构建弹性、可靠、协调的
- 1、回顾一下大家有没有注意到,目前讲到的所有 controller 中的方法接收到请求之后,都是有返回值的,返回值主要有 2 种类型:1、
- 目录概览问题一原因解决办法问题二原因解决办法概览在当下几乎所有的公司都采用了前后端分离的开发模式,Swagger作为了在API在线文档工具,
- 本文实例为大家分享了Unity实现俄罗斯方块游戏的具体代码,供大家参考,具体内容如下一、演示二、实现思路创建每一个方块可移动到的位置点,可以
- 话不多说,请看代码:/// <summary> /// 获取客户端IP /// </summary
- 目录一、Actuator简介二、与SpringBoot2.0整合 1、核心依赖Jar包2、Yml配置文件三、监控接口详解 
- 目录Directory:创建文件夹删除文件夹获取文件夹下的子文件夹获取同类型的文件判断文件夹是否存在移动文件夹总结之前发过File对文件的操
- 本文实例为大家分享了Flutter自定义底部导航栏的具体代码,供大家参考,具体内容如下文件结构:main.dartimport 'p
- 前言将Chart的X轴设置为时间轴是一个说简单不简单的问题,说难也不难的问题,你用过之后呢就感觉很容易,你没用过呢,就比较难,所以这个是很值
- 问题:在项目中,当保存数据超过数据库字段列长度限制时,如何解决?一种常见的解决办法是:截串存取。顾名思义,就是对大文本数据按指定长度进行截取
- 本文实例为大家分享了利用Swing绘制一个动态时钟的具体代码,供大家参考,具体内容如下效果代码在下面,可跳过解析。前言编程实现一个时钟利用S
- 1、如何解决服务之间的通信问题?[1]HTTP REST方式 使用http协议进行数据传递 json格式数据[2]RPC方式 远程过程调用
- 1.Knife4j在线API文档基本使用Knife4j是一款基于Swagger 2的在线API文档框架。使用Knife4j的基础步骤:添加依
- 本文讲述了Android编程中关于单线程模型的理解与分析。分享给大家供大家参考,具体如下:当一个Android程序启动时,Android系统
- 介绍:一个实现了下拉刷新,滚动到底部加载更多以及添加header功能的的RecyclerView。使用方式和RecyclerView完全一致
- 前言Java 语言很强大,但是,有人的地方就有江湖,有猿的地方就有 bug,Java 的核心代码并非十全十美。比如在 JDK 中居然也有反模
- @schedule 注解 是springboot 常用的定时任务注解,使用起来简单方便,但是如果定时任务非常多,或者有的任务很耗时
- Java io简介Java io系统的设计初衷,就是为了实现“文件、控制台、网络设备”这些io设置的通信。例如,对于一个文件,我们可以打开文
- 美团电商应用平台大家使用非常频繁,下面小编通过本文给大家介绍电商应用平台中常用的选择类别下拉列表的实现。先给大家展示下效果图:一、下拉列表的
- Rsa加密RSA是目前最有影响力的公钥加密算法,RSA也是第一个既能用于数据加密也能用于数字签名的算法。该算法基于一个十分简单的数论事实:将