当前位置: 首页 > news >正文

springboot 自定义注解 ,实现接口限流(计数器限流)【强行喂饭版】

思路:通过AOP拦截注解标记的方法,在Redis中维护一个计数器来记录接口访问的频率,
并根据限流策略来判断是否允许继续处理请求。

另一篇:springboot 自定义注解 ,aop切面@Around; 为接口实现日志插入【强行喂饭版】

不多说,直接上代码:

一:创建限流类型

/*** 限流类型* */public enum LimitType
{/*** 默认策略全局限流*/DEFAULT,/*** 根据请求者IP进行限流*/IP
}


二:创建注解

import 你上面限流类型的路径.LimitType;import java.lang.annotation.*;/*** 限流注解* */
// 注解的作用目标为方法
@Target(ElementType.METHOD) // 注解在运行时保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用
@Retention(RetentionPolicy.RUNTIME) // 指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值
@Documented 
public @interface LimiterToShareApi{/*** 限流key*/public String key() default "";/*** 限流时间,单位秒*/public int time() default 60;/*** 限流次数*/public int count() default 100;/*** 限流类型,默认全局限流*/public LimitType limitType() default LimitType.DEFAULT;
}


**三:编写业务异常类 **

/*** 业务异常* */
public final class ServiceException extends RuntimeException
{// 序列化的版本号的属性private static final long serialVersionUID = 1L;/*** 错误码*/private Integer code;/*** 错误提示*/private String message;/*** 空构造方法,避免反序列化问题*/public ServiceException(){}/*** 异常信息*/public ServiceException(String message){this.message = message;}}


四:实现aop切面拦截,限流逻辑处理

import 你上面限流类型的路径.LimitType;
import 你上面业务异常的路径.ServiceException;
import 你上面限流注解的路径.LimiterToShareApi;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;// 声明这是一个切面类
@Aspect
// 表明该类是一个组件,将该类交给spring管理。
@Component
// 指定执行顺序,值越小,越先执行。限流策略一般最先执行。
@Order(1) 
public class LimiterToShareApiAspect {// 记录日志的Logger对象private static final Logger log = LoggerFactory.getLogger(LimiterToShareApiAspect.class);// 操作Redis的RedisTemplate对象private RedisTemplate<Object, Object> redisTemplate;//在Redis中执行Lua脚本的对象private RedisScript<Long> limitScript;@Autowiredpublic void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate) {this.redisTemplate = redisTemplate;}@Autowiredpublic void setLimitScript(RedisScript<Long> limitScript) {this.limitScript = limitScript;}// 这个注解作用及普及 见文章后面解析@Before("@annotation(limiter)")public void doBefore(JoinPoint point, LimiterToShareApi limiter) throws Throwable {// 根据业务需求,看看是否去数据库查询对应的限流策略,还是直接使用注解传递的值// 这里演示为 获取注解的值int time = limiter.time();int count = limiter.count();String combineKey = getCombineKey(limiter, point);List<Object> keys = Collections.singletonList(combineKey);try {Long number = redisTemplate.execute(limitScript, keys, count, time);if (number == null || number.intValue() > count) {throw new ServiceException("限流策略:访问过于频繁,请稍候再试");}log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);} catch (ServiceException e) {throw e;} catch (Exception e) {throw new RuntimeException("服务器限流异常,请稍候再试");}}/*** 获取用于限流的组合键,根据LimterToShareApi注解和JoinPoint对象来生成。** @param rateLimiter LimiterToShareApi注解,用于获取限流配置信息。* @param point       JoinPoint对象,用于获取目标方法的信息。* @return 生成的用于限流的组合键字符串。*/public String getCombineKey(LimiterToShareApi rateLimiter, JoinPoint point) {// 创建一个StringBuffer用于拼接组合键StringBuffer stringBuffer = new StringBuffer(rateLimiter.key() + "-");// 根据LimterToShareApi注解的limitType判断是否需要添加IP地址信息到组合键中【判断限流类型 是否根据ip进行限流】if (rateLimiter.limitType() == LimitType.IP) {// 如果需要添加IP地址信息,调用IpUtils.getIpAddr()方法获取当前请求的IP地址,并添加到组合键中stringBuffer.append(getClientIp()).append("-");}// 使用JoinPoint对象获取目标方法的信息MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();// 将目标方法所属类的名称和方法名称添加到组合键中stringBuffer.append(targetClass.getName()).append("-").append(method.getName());// 返回生成的用于限流的组合键字符串return stringBuffer.toString();}/*** 获取调用方真实ip [本机调用则得到127.0.0.1]* 首先尝试从X-Forwarded-For请求头获取IP地址,如果没有找到或者为unknown,则尝试从X-Real-IP请求头获取IP地址,* 最后再使用request.getRemoteAddr()方法作为备用方案。注意,在多个代理服务器的情况下,* X-Forwarded-For请求头可能包含多个IP地址,我们取第一个IP地址作为真实客户端的IP地址。*/public String getClientIp() {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();String ipAddress = request.getHeader("X-Forwarded-For");if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("X-Real-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddr();}// 多个代理服务器时,取第一个IP地址int index = ipAddress.indexOf(",");if (index != -1) {ipAddress = ipAddress.substring(0, index);}return ipAddress;}}

五:哪里需要点哪里

@PostMapping("/接口api")
// 根据自己业务选择是否需要这些参数,如果是想从数据库读取,不填参数即可
// 这里意思为对key的限制为 全局 每60秒内2次请求,超过2次则限流
@LimiterToShareApi(key = "key",time = 60,count = 100,limitType = LimitType.DEFAULT)
public AjaxResult selectToUserId(参数){}


限流(代码)结果:
在这里插入图片描述在这里插入图片描述在这里插入图片描述


解析:

@Before("@annotation(limiter)")- 使用了@Before 来表示这是一个切面注解,用于定义在目标方法执行前执行的逻辑- @annotation(limiter) 中的limiter是指参数名称,而不是注解名称。- @annotation(limiter) 中的limiter参数类型为LimiterToShareApi,
表示你将拦截被@LimiterToShareApi注解标记的方法,并且可以通过这个参数来获取@LimiterToShareApi注解的信息。
如果你想拦截其他注解,只需将第二个参数的类型修改为对应的注解类型即可。

普及:

JoinPointSpring AOP中的一个接口,它代表了在程序执行过程中能够被拦截的连接点(Join Point)。
连接点指的是在应用程序中,特定的代码块,比如方法的调用、方法的执行、构造器的调用等。JoinPointAOP中的作用是用于传递方法调用的信息,比如方法名、参数、所属的类等等。
当AOP拦截到一个连接点时,就可以通过JoinPoint对象来获取这些信息,并根据需要进行相应的处理。在AOP中,常见的通知类型(advice)如下:@Before:在目标方法执行之前执行。
@After:在目标方法执行之后(无论是否抛出异常)执行。
@AfterReturning:在目标方法成功执行之后执行。
@AfterThrowing:在目标方法抛出异常后执行。
@Around:在目标方法执行前后都执行,可以控制目标方法的执行。在以上各种通知中,可以使用JoinPoint参数来获取连接点的相关信息。
例如,在@Around通知中,可以使用JoinPoint对象来获取目标方法的信息,
比如方法名、参数等。这样,我们就可以根据这些信息来实现我们需要的切面逻辑。eg:
// 获取方法名
String methodName = joinPoint.getSignature().getName();//获取方法参数
Object[] args = joinPoint.getArgs();// 获取所属类名
String className = joinPoint.getSignature().getDeclaringTypeName();// 获取源代码位置信息
SourceLocation sourceLocation = joinPoint.getSourceLocation();

相关文章:

springboot 自定义注解 ,实现接口限流(计数器限流)【强行喂饭版】

思路&#xff1a;通过AOP拦截注解标记的方法&#xff0c;在Redis中维护一个计数器来记录接口访问的频率&#xff0c; 并根据限流策略来判断是否允许继续处理请求。 另一篇&#xff1a;springboot 自定义注解 &#xff0c;aop切面Around&#xff1b; 为接口实现日志插入【强行喂…...

istio安装部署总结

istio安装部署总结 大纲 istio基础概念版本选择安装istio核心主件卸载istiokiali安装 istio基础概念 https://istio.io/latest/zh/docs/ 中文文档 istio是一个服务治理平台&#xff0c;治理服务间的访问&#xff0c;&#xff08;例如流量控制&#xff0c;安全策略&#xf…...

Linux操作系统~必考面试题⑨

1、rpm 命令 Linux rpm 命令用于管理套件。 rpm(redhat package manager) 原本是 Red Hat Linux 发行版专门用来管理Linux 各项套件的程序&#xff0c;由于它遵循 GPL 规则且功能强大方便&#xff0c;因而广受欢迎。逐渐受到其他发行版的采用。 RPM 套件管理方式的出现&…...

国标GB28181协议视频平台EasyCVR修改录像计划等待时间较长的原因排查与解决

音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、H.265自动转码H.264、平台级联等。为了便于用户二次开发、调用与集成&…...

线性代数(主题篇):第三章:向量组 、第四章:方程组

文章目录 第3章 n维向量1.概念(1)n维单位列向量 2.向量、向量组的的线性关系(线性相关性)(1)线性表示 &#xff1a;AXβ(2)线性相关、线性无关&#xff1a; AX0①线性相关②线性无关③线性相关性7大定理 3.极大线性无关组、等价向量组、向量组的秩1.极大线性无关组2.等价向量组…...

大数据课程C4——ZooKeeper结构运行机制

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解Zookeeper的特点和节点信息&#xff1b; ⚪ 掌握Zookeeper的完全分布式安装 ⚪ 掌握Zookeeper的选举机制、ZAB协议、AVRO&#xff1b; 一、Zookeeper-简介 1. 特点…...

解决伪类元素‘after‘或者‘before‘遮挡父元素,导致鼠标移入或点击等事件不生效的问题

第一种调整css的index值 如果对显示没有影响的话&#xff0c;可以这么做 第二种设置css属性&#xff1a;pointer-event&#xff1a;none 原理是&#xff1a; 对一个元素设置 pointer-events: none&#xff0c;能让浏览器在处理鼠标操作时&#xff0c;忽视掉这个元素的存在&a…...

电动汽车市场的减速,正在让小鹏汽车付出代价

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;由于价格压力上升、竞争加剧和需求减弱&#xff0c;小鹏汽车的交付量出现了明显下滑&#xff0c;6月份的交付量已经同比下降了43%。 &#xff08;2&#xff09;小鹏汽车对2023年…...

Yarn上Streaming流自动调节资源设计

Streaming流自动调节资源 自动资源调节简单来说就是根据数据的输入速率和数据的消费速率来判断是否应该调节资源。如果输入速率大于消费速率&#xff0c;并且在输入速率还在攀升&#xff0c;则将该Job停止并调高Job的资源等级然后重启。如果消费速率大于输入速率&#xff0c;并…...

微信小程序的个人博客--【小程序花园】

微信目录集链接在此&#xff1a; 详细解析黑马微信小程序视频–【思维导图知识范围】难度★✰✰✰✰ 不会导入/打开小程序的看这里&#xff1a;参考 让别人的小程序长成自己的样子-更换window上下颜色–【浅入深出系列001】 文章目录 本系列校训啥是个人博客项目里的理论知识…...

智慧园区楼宇合集 | 图扑数字孪生管控系统

智慧园区是指将物联网、大数据、人工智能等技术应用于传统建筑和基础设施&#xff0c;以实现对园区的全面监控、管理和服务的一种建筑形态。通过将园区内设备、设施和系统联网&#xff0c;实现数据的传输、共享和响应&#xff0c;提高园区的管理效率和运营效益&#xff0c;为居…...

【代码随想录day21】二叉搜索树中的众数

题目 给你一个含重复值的二叉搜索树&#xff08;BST&#xff09;的根节点 root &#xff0c;找出并返回 BST 中的所有 众数&#xff08;即&#xff0c;出现频率最高的元素&#xff09;。 如果树中有不止一个众数&#xff0c;可以按 任意顺序 返回。 假定 BST 满足如下定义&am…...

【防火墙】iptables防火墙(一)

防火墙具有隔离功能 主要部署在网络边缘或者主机边缘&#xff0c;防火墙的主要作用是决定哪些数据可以被外网访问&#xff0c;哪些数据可以进入内网访问 网络层&#xff08;路由器&#xff09;&#xff1a;数据的转发 安全技术 1.入侵监测系统&#xff1a;在检测到威胁&…...

微信小程序之富文本特殊处理

文章目录 前言一、video的处理二、img的处理总结 前言 小程序中使用富文本编辑器&#xff0c;由于rich-text受限 部分富文本内容无法渲染或排版错乱。以img和video为例&#xff0c;处理起来让人头疼。网上各种长篇大论&#xff0c;实际上没有任何帮助。接下来我们就一起聊聊im…...

react-draft-wysiwyg富文本编辑器

在React项目中使用 yarn add react-draft-wysiwyg draft-js or npm i react-draft-wysiwyg draft-js推荐在项目中单独创建一个富文本编辑器组件 import { Editor } from "react-draft-wysiwyg"; import { EditorState, convertToRaw, ContentState } from draft-js…...

P5721 【深基4.例6】数字直角三角形

【深基4.例6】数字直角三角形 题目描述 给出 n n n&#xff0c;请输出一个直角边长度是 n n n 的数字直角三角形。所有数字都是 2 2 2 位组成的&#xff0c;如果没有 2 2 2 位则加上前导 0 0 0。 输入格式 输入一个正整数 n n n。 输出格式 输出如题目要求的数字直…...

【电子设计大赛】2023 年全国大学生电子设计竞赛 仪器和主要元器件清单

2023 年全国大学生电子设计竞赛仪器设备和主要元器件及器材清单 [本科组] 1. 仪器设备清单 直流稳压电源&#xff08;具有恒流/恒压模式自动切换功能&#xff0c;0~30V/3A&#xff0c;双路&#xff09; 数字示波器&#xff08;100MHz&#xff0c; 双通道&#xff09; 函数发…...

(八九)如何与InfluxDB交互InfluxDB HTTP API

以下内容来自 尚硅谷&#xff0c;写这一系列的文章&#xff0c;主要是为了方便后续自己的查看&#xff0c;不用带着个PDF找来找去的&#xff0c;太麻烦&#xff01; 第 8 章 前言&#xff1a;如何与InfluxDB交互 1、InfluxDB启动后&#xff0c;会向外提供一套HTTP API。外部程…...

excel 生成sql技巧

"update 表名 set 字段名"&A2&" where 字段名"&B2&";"...

2023牛客暑期多校训练营2(D/E/F/H/I/K)

目录 D.The Game of Eating E.Square F.Link with Chess Game H.0 and 1 in BIT I.Link with Gomoku K.Box D.The Game of Eating 思路&#xff1a;倒着贪心。因为正着贪会导致一种局面&#xff1a;我选了当前喜爱值最大的菜&#xff0c;但是就算我不选这个菜&#xff0…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...

基于PHP的连锁酒店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

MeshGPT 笔记

[2311.15475] MeshGPT: Generating Triangle Meshes with Decoder-Only Transformers https://library.scholarcy.com/try 真正意义上的AI生成三维模型MESHGPT来袭&#xff01;_哔哩哔哩_bilibili GitHub - lucidrains/meshgpt-pytorch: Implementation of MeshGPT, SOTA Me…...

Pandas 可视化集成:数据科学家的高效绘图指南

为什么选择 Pandas 进行数据可视化&#xff1f; 在数据科学和分析领域&#xff0c;可视化是理解数据、发现模式和传达见解的关键步骤。Python 生态系统提供了多种可视化工具&#xff0c;如 Matplotlib、Seaborn、Plotly 等&#xff0c;但 Pandas 内置的可视化功能因其与数据结…...