SpringBoot项目实现自定义注解方式的接口限流

一,实现原理

该限流方式使用的是令牌桶算法,令牌桶算法是基于漏桶算法的一种改进,主要在于令牌桶算法能够在限制服务调用的平均速率的同时,还能够允许一定程度内的突发调用。

  1. 系统以固定的速率向桶中添加令牌
  2. 当有请求到来时,会尝试从桶中移除一个令牌,如果桶中有足够的令牌,则请求可以被处理或数据包可以被发送;
  3. 如果桶中没有令牌,那么请求将被拒绝;
  4. 桶中的令牌数不能超过桶的容量,如果新生成的令牌超过了桶的容量,那么新的令牌会被丢弃。
  5. 令牌桶算法的一个重要特性是,它能够应对突发流量。当桶中有足够的令牌时,可以一次性处理多个请求,这对于需要处理突发流量的应用场景非常有用。但是又不会无限制的增加处理速率导致压垮服务器,因为桶内令牌数量是有限制的。

如图所示:
在这里插入图片描述

二,代码实现

Guava中的RateLimiter就是基于令牌桶实现的,可以直接拿来使用。
1. 引入依赖

		<!--aop切面依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>33.1.0-jre</version></dependency>

2. 创建注解

/*** 限流注解*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {/*** 限流key*/String key() default "";/*** 限流时间,单位秒*/int time() default 1;/*** 限流次数*/int count() default 2;/*** 时间类型*/TimeUnit timeUnit() default TimeUnit.SECONDS;/*** 提示消息*/String message() default "系统繁忙,请稍后重试";
}

3. AOP切面实现

该切面实现只对接口进行进行限流。

/*** 限流切面处理*/
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {private final Map<String, RateLimiter> rateLimiterMap = Maps.newConcurrentMap();/*** 切面方法,注解之前执行*/@Around("@annotation(rateLimit)")public Object doBefore(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {// key的作用,不同的接口,不同的限流控制String key = rateLimit.key();RateLimiter rateLimiter;if (!rateLimiterMap.containsKey(key)){// 创建令牌桶,设置每秒发送得令牌rateLimiter = RateLimiter.create(rateLimit.count());rateLimiterMap.put(key, rateLimiter);log.info("新建了令牌桶={},容量={}", key, rateLimit.count());}rateLimiter = rateLimiterMap.get(key);// 获取令牌,在规定的时间获取令牌,获取不到返回falseboolean acquire = rateLimiter.tryAcquire(rateLimit.time(), rateLimit.timeUnit());// 拿不到令牌,直接返回异常信息if (!acquire){log.error("令牌桶={},获取令牌失败", key);throw new RuntimeException(rateLimit.message());}return point.proceed();}

根据接口参数来区分接口限流的切面实现,参数可以是用户id或者ip地址,这样就实现了具体用户或ip限流

/*** 限流切面处理*/
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {/*** 方法参数解析器*/private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();private final Map<String, RateLimiter> rateLimiterMap = Maps.newConcurrentMap();/*** 切面方法,注解之前执行*/@Around("@annotation(rateLimit)")public Object doBefore(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {// 获取方法(通过方法签名来获取)MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();// 获取参数值Object[] args = point.getArgs();// 获取方法上参数的名称String[] parameterNames = pnd.getParameterNames(method);String parameter = "";for (int i = 0; i < parameterNames.length; i++) {String parameterName = parameterNames[i];if (parameterName.equals("param")){parameter = (String) args[i];}}// key的作用,不同的接口,不同的流量控制String key = rateLimit.key() + parameter ;RateLimiter rateLimiter;if (!rateLimiterMap.containsKey(key)){// 创建令牌桶,设置每秒发送得令牌rateLimiter = RateLimiter.create(rateLimit.count());rateLimiterMap.put(key, rateLimiter);log.info("新建了令牌桶={},容量={}", key, rateLimit.count());}rateLimiter = rateLimiterMap.get(key);// 获取令牌,在规定的时间获取令牌,获取不到返回falseboolean acquire = rateLimiter.tryAcquire(rateLimit.time(), rateLimit.timeUnit());// 拿不到令牌,直接返回异常信息if (!acquire){log.error("令牌桶={},获取令牌失败", key);throw new RuntimeException(rateLimit.message());}return point.proceed();}

3. 注解应用

    @RateLimit(key = "index", count = 5)@GetMapping("/index/{param}")public String index(@PathVariable("param") String param){return param + "hello world!";}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/846002.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软设之快速排序

快速排序是冒泡排序的改进算法 它采用的是分治法&#xff0c;基本思想是把原问题分解为若干规模更小但结构与原问题相似的子问题&#xff0c;通过递归解决这些子问题&#xff0c;然后将这些子问题的解组合成原问题的解。 它的步骤是 1.在待排序的n个记录中任取一个记录&…

张大哥笔记:你卖什么,就反着来卖

普通人打工的一生&#xff0c;就是努力工作&#xff0c;买房&#xff0c;买车&#xff0c;送孩子上好的学校&#xff0c;为了孩子不要输在起跑线上&#xff0c;拼命报各种补习班等&#xff0c;这些都是普通人认为的主流价值观文化&#xff0c;也造就了一批批的赚钱机器&#xf…

带DSP音效处理D类数字功放TAS5805M中文资料

国产替代D类数字功放中文资料访问下方链接 ACM8628 241W立体声182W单通道数字功放中文寄存器表 内置DSP多种音频处理效果ACM8628M-241W立体声或182W单通道数字功放 1 特性 具有增强处理能力和低功率损耗的 TAS5805M 23W、无电感器、数字输入、立体声、闭环 D 类音频放大器 …

华为设备配置静态路由和默认路由

华为设备配置静态路由和默认路由 理论部分知识&#xff1a; 路由分为两个大类&#xff1a;静态路由-----动态路由 静态路由&#xff1a;手工指定&#xff0c;适用于小规模的网络应用场景&#xff0c;如果网络规模变大&#xff0c;这样的方式非常不适合而且容易出错。 语法&…

java单元测试如何断言异常

​ 在junit单元测试中&#xff0c;我们可以使用 org.junit.Assert.assertThrows 包下的 assertThrows() 方法 这个方法返回了一个泛型的异常 public static <T extends Throwable> assertThrows(Class<T> expectedType, Executable executable)​ 假设我们有以下…

Java之IO流

一、引言 &#xff08;1&#xff09;解释&#xff1a; i&#xff1a;input &#xff08;输入&#xff09; o&#xff1a;output &#xff08;输出&#xff09; &#xff08;2&#xff09;图解 注意&#xff1a; 1、Xxx 这个程序一旦在桌面关闭掉了&#xff0c;也就是运行完…

动态路由OSPF单区域和多区域配置实验

动态路由OSPF的配置 OSPF分类两种情况&#xff1a;单区域 多区域路由 OSPF单区域路由配置 OSPF&#xff1a;开放最短路径优先的路由协议。属于大型动态路由协议&#xff0c;适用于中大型的园区网。 网络拓扑&#xff1a; 配置步骤&#xff1a; 1.完成基本配置&#xff08;略&…

能耗监测系统在上海交通大学闵行校区理科实验楼群的设计与应用

引言 建筑能耗系统&#xff0c;除了基本的电力参数监测、配电系统的运行状况&#xff0c;更加关注能耗的去向。除了常规的园区楼层出线电能计量&#xff0c;还会涉及水&#xff0c;气等能耗计量。 针对上海交通大学闵行校区理科实验楼群能耗监测系统的具体要求&#xff0c;以…

android-AP6212配置

一、调试背景 硬件平台a33 系统android wifi:ap6212a 从内核日志看到模块能加载识别到fw_bcm43438a1.bin ,但是nvram加载的配置是nvram_ap6212.txt&#xff0c;虽然能正常工作&#xff0c;但是对比了一下nvram_ap6212.txt 和nvram_ap6212a.txt还是有差异的 内核日志&#xff1…

依赖管理包介绍

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 相关组件 3. 示例代码4. 内容总结 我们在上一章回中介绍了"使用get进行依赖管理"相关的内容&#xff0c;本章回中将介绍如何使用get进行状态管理一.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 …

C语言:如何在微软VS环境下使用C库?

技术答疑流程 扫描二维码&#xff0c;添加个人微信&#xff1b;支付一半费用&#xff0c;获取答案&#xff1b;如果满意&#xff0c;则支付另一半费用&#xff1b; 知识点费用&#xff1a;10元 项目费用&#xff1a;如果有项目任务外包需求&#xff0c;可以微信私聊

Typescript高级: 深入理解ConstructorParameters类型及借助infer构建带参数的工厂实例方法

概述 在TypeScript中&#xff0c;利用泛型和类型推断&#xff0c;我们可以编写出既灵活又安全的代码特别是在处理类和其实例化过程中&#xff0c;这些特性显得尤为重要我们着重关注构造函数参数&#xff08;constructor parameters&#xff09;的类型处理以及如何利用泛型工厂…

IP路由策略1

控制层面:路由协议传递路由信息的流量--对应的方向 数据层面:设备间具体访问时请求的流量--对应方向 控制层面方向与数据层面方向一定相反 在控制层面流量进或出的接口上&#xff0c;抓取流量后&#xff0c;修改其中参数或删除该信息&#xff0c;最终起到影响路由器路由表的生…

「Django秘境探险:揭开Web开发的神秘面纱」

大家好&#xff0c;我是阿佑&#xff0c;今天将和大家一块学习到如何利用Django框架的高级特性&#xff0c;构建出既快速又安全的Web应用。我们将一起破解Django的内部机制&#xff0c;掌握从数据模型到模板设计的每一个环节。准备好了吗&#xff1f;Let’s go &#xff01; 文…

“手撕”链表的九道OJ习题

目录 1. 第一题 2. 第二题 3. 第三题 4. 第四题 5. 第五题 6. 第六题 7. 第七题 8. 第八题 9. 第九题 1. 第一题 删除链表中等于给定值 val 的所有节点。OJ链接 思路如下&#xff1a; 相当于链表的removeAll();制定prev和cur&#xff0c;prev记录前一个节点&#xff…

MySQL学习——行分类及日期计算

1 行分类 你可能已经在前面的例子中注意到&#xff0c;结果行的显示没有特定的顺序。当结果行以某种有意义的方式进行排序时&#xff0c;检查查询输出通常会更容易。要对结果进行排序&#xff0c;请使用ORDER BY子句。 以下是按日期排序的动物生日&#xff1a; mysql> SE…

Windows11 wsl2编译Android14 使用ASfP Debug windows上启动的模拟器

wsl2的安装和配置 安装&#xff1a; 直接百度搜索最新的wsl2安装教程即可&#xff0c;官网&#xff1a;https://learn.microsoft.com/zh-cn/windows/wsl/install 1. 启用适用于 Linux 的 Windows 子系统(以管理员身份打开 PowerShell 并运行) Enable-WindowsOptionalFeature…

Nginx的Map模块

Nginx的map模块是一个功能强大的工具&#xff0c;可以在配置Nginx时实现更高效的请求处理。本文将介绍map模块的基本用法、使用场景、示例以及注意事项。 什么是Nginx的map模块&#xff1f; Nginx的map模块允许我们根据变量的值来映射到对应的值。这个映射可以是静态的&#…

8-Django项目--登录及权限

目录 templates/login/login.html templates/login/404.html views/login.py utils/pwd_data.py auth.py settings.py 登录及权限 登录 views.py 中间件 auth.py templates/login/login.html {% load static %} <!DOCTYPE html> <html lang"en"&g…

Linux系统编程——动静态库

目录 一&#xff0c;关于动静态库 1.1 什么是库&#xff1f; 1.2 认识动静态库 1.3 动静态库特征 二&#xff0c;静态库 2.1 制作静态库 2.2 使用静态库 三&#xff0c;动态库 3.1 制作动态库 3.2 使用动态库一些问题 3.3 正确使用动态库三种方法 3.3.1 方法一&…