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;买车&#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之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;以…

依赖管理包介绍

文章目录 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;可以微信私聊

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…

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…

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 方法一&…

什么是BFC?

1、BFC&#xff1a;又叫块级格式化上下文&#xff08;block formatting context&#xff09;&#xff0c;就是页面上一个隔离的独立容器&#xff0c;容器里面的子元素不会影响到外面的元素。 2、BFC的原则&#xff1a;如果一个元素具有BFC&#xff0c;那么内部元素再怎么弄&am…

QuillEditor富文本结合vue3使用,可单独抽离成组件,富文本的内容可自定义

实现效果&#xff1a; 上方的粗体、斜体、字号、字体等各种信息支持自定义配置。 实现方式&#xff1a; 下面的介绍为分步骤的详细介绍&#xff0c;完整版纯享代码可参考这篇博客富文本QuillEditorvue3组件代码纯享版-CSDN博客 1.新建一个新文件--子组件&#xff0c;如命名为…

git应用最佳实践

插&#xff1a; AI时代&#xff0c;程序员或多或少要了解些人工智能&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家(前言 – 人工智能教程 ) 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家…

QGraphicsView实现简易地图19『迁徙图』

模仿echarts的迁徙图效果 用到了前2篇制作的散点(涟漪效果)和两年前的路径动画类&#xff1b;然尾迹效果未依附路径&#xff0c;有待优化。 动态演示效果 静态展示图片 核心代码 #pragma once #include "Item/AbstractGeoItem.h" #include "DataStruct/GeoD…

php之文件操作代码审计

1 PHP文件操作函数 1.1 PHP文件操作函数 文件包含 include/require/include_once/require_once 文件读取 file_get_contents/fread/readfile/file 文件写入 file_put_contents/fwrite/mkdir/fputs 文件删除 unlink/rmdir 文件上传 move_uploaded_file/copy/rename 1.2 文…

IO流(1)

定义&#xff1a;存取和读取数据的解决方案 作用&#xff1a;用于读写数据&#xff08;本地文件、网络&#xff09; 分类&#xff1a; 一种是&#xff1a;输出流和输入流。 一种是&#xff1a;字节流和字符流。 字节流 字节流——FileOutputStream&#xff08;字节输出流&…