SpringBoot使用滑动窗口限流防止用户重复提交(自定义注解实现)

        在你的项目中,有没有遇到用户重复提交的场景,即当用户因为网络延迟等情况把已经提交过一次的东西再次进行了提价,本篇文章将向各位介绍使用滑动窗口限流的方式来防止用户重复提交,并通过我们的自定义注解来进行封装功能。

首先,导入相关依赖:

<!--        引入切面依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><scope>test</scope></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency>

然后,我们先写一下滑动窗口限流的逻辑:

//滑动窗口限流逻辑
public class RateLimiter {private static ConcurrentHashMap<String, Deque<Long>> requestTimestamps=new ConcurrentHashMap<>();public static boolean isAllowed(String userId,int timeWindow,int maxRequests){long now =System.currentTimeMillis();long windowStart=now -(timeWindow*1000);requestTimestamps.putIfAbsent(userId,new LinkedList<>());Deque<Long> timestamps=requestTimestamps.get(userId);synchronized (timestamps){// 移除窗口外的时间戳while(!timestamps.isEmpty()&& timestamps.peekFirst()<windowStart){timestamps.pollFirst();}// 如果时间戳数量小于最大请求数,允许访问并添加时间戳if(timestamps.size()<maxRequests){timestamps.addLast(now);return true;}else{return false;}}}
}
主要部分解释
1. 定义 requestTimestamps 变量

private static ConcurrentHashMap<String, Deque<Long>> requestTimestamps = new ConcurrentHashMap<>();

  • requestTimestamps 是一个并发的哈希映射,用于存储每个用户的请求时间戳。
  • 键(String)是用户ID。
  • 值(Deque<Long>)是一个双端队列,用于存储用户请求的时间戳(以毫秒为单位)。
2. isAllowed 方法

public static boolean isAllowed(String userId, int timeWindow, int maxRequests) {

  • 该方法接受三个参数:
    • userId:用户ID。
    • timeWindow:时间窗口,单位为秒。
    • maxRequests:时间窗口内允许的最大请求数。
  • 方法返回一个布尔值,表示用户是否被允许发出请求。
3. 获取当前时间和时间窗口开始时间

long now = System.currentTimeMillis(); long windowStart = now - (timeWindow * 1000);

  • now:当前时间,以毫秒为单位。
  • windowStart:时间窗口的开始时间,即当前时间减去时间窗口长度,以毫秒为单位。
4. 初始化用户的请求时间戳队列

requestTimestamps.putIfAbsent(userId, new LinkedList<>()); Deque<Long> timestamps = requestTimestamps.get(userId);

  • requestTimestamps.putIfAbsent(userId, new LinkedList<>()):如果 requestTimestamps 中没有该用户的记录,则为其初始化一个空的 LinkedList
  • timestamps:获取该用户对应的时间戳队列。
5. 同步时间戳队列

synchronized (timestamps) {

  • 同步块:对用户的时间戳队列进行同步,以确保线程安全。
6. 移除窗口外的时间戳

while (!timestamps.isEmpty() && timestamps.peekFirst() < windowStart) { timestamps.pollFirst(); }

  • 循环检查并移除队列中位于时间窗口之外的时间戳(即小于 windowStart 的时间戳)。
7. 检查请求数并更新时间戳队列

if (timestamps.size() < maxRequests) { timestamps.addLast(now); return true; } else { return false; }

  • 如果时间戳队列的大小小于 maxRequests,说明在时间窗口内的请求次数未超过限制:
    • 将当前时间戳添加到队列的末尾。
    • 返回 true,表示允许请求。
  • 否则,返回 false,表示拒绝请求。

接下来我们需要实现一个AOP切面,来实现我们的自定义注解

@Component
@Aspect
public class RateLimitInterceptor {
//    private HashMap<String,String> info;@Autowiredprivate RedisTemplate<String,String> redisTemplate;@Around("@annotation(rateLimit)")public Object interceptor(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {String userid= redisTemplate.opsForValue().get("loginId");   //获取用户IDSystem.out.println("userid:"+userid);int timeWindow=rateLimit.timeWindow();int maxRequests=rateLimit.maxRequests();if(RateLimiter.isAllowed(userid,timeWindow,maxRequests)){return joinPoint.proceed();}else{throw new RepeatException("访问过于频繁,请稍后再试");}}
}

        获取用户ID的逻辑需要根据你的项目实际情况进行编写,我这里是把id存在redis里面的,但是也是存在问题的,读者可以尝试使用RabbitMQ进行实现。

然后,自定义一个注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {int timeWindow() default 60; // 时间窗口大小,单位为秒int maxRequests() default 10;  //最大请求次数
}

        以上代码写好之后,其实整个关键的代码就完成了,你可以随便在你的项目中找一个接口试一下,如下:

maxRequests表示在timeWindow时间内的最大请求数

        结果如下,当然如果需要在前台显示,可以稍微改一下异常的处理方式,让提示信息能在前台显示:

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

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

相关文章

代码随想录算法训练营第三十三天|452. 用最少数量的箭引爆气球、 435. 无重叠区间、 763.划分字母区间

452. 用最少数量的箭引爆气球 题目链接&#xff1a;452. 用最少数量的箭引爆气球 文档讲解&#xff1a;代码随想录 状态&#xff1a;没想出来 思路&#xff1a;对气球终点位置排序&#xff0c;从第一个气球终点位置射出箭&#xff0c;看这支箭可以尽可能穿过几个气球&#xff0…

Excel 宏录制与VBA编程 —— 12、日期相关

代码1 - 获取当前时间日期信息 代码2 - 时间日期格式 代码3 - 时间日期计算 代码4 - 时间日期案例 关注 笔者 - jxd

免费悬浮翻译器哪个好?测评5款悬浮翻译器

在享受休闲时光时&#xff0c;我们通常都希望不被打扰&#xff0c;对吧&#xff1f; 然而&#xff0c;有时打扰我们的并非是外界的干扰&#xff0c;而是在观看外语视频时&#xff0c;无法理解视频内容的烦躁感。 不过&#xff0c;今天本文将为大家揭开几款屏幕悬浮翻译软件的…

基于Java协同过滤算法的图书推荐系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

ctr/cvr预估之WideDeep模型

ctr/cvr预估之Wide&Deep模型 在探索点击率&#xff08;CTR&#xff09;和转化率&#xff08;CVR&#xff09;预估的领域中&#xff0c;我们始终追求的是一种既能捕获数据中的线性关系&#xff0c;又能发现复杂模式的模型。因子分解机&#xff08;Factorization Machines, …

微服务SpringCloud,SpringAliBaBa(2)

微服务02 1.网关路由 网络的关口&#xff0c;负责请求的路由、转发、身份校验。 有了网关之后&#xff0c;微服务的地址不用在暴露了&#xff0c;就暴露个网关地址。 快速入门 routes代表一套路由&#xff0c;pridicates是规则&#xff0c;对请求做出判断&#xff0c;看是哪…

[C++深入] --- malloc/free和new/delete

1 new运算符的拓展 1.1 自由存储区与堆的概念 在C++中,内存区分为5个区,分别是堆、栈、自由存储区、全局/静态存储区、常量存储区。 自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。 new操作符从自由存储区(free st…

今日头条豆包大语言模型api接入python SDK,安装官方库报错,解决方法

豆包python大语言模型库安装指令&#xff1a; pip install volcengine-python-sdk 报错&#xff1a; note: This error originates from a subprocess, and is likely not a problem with pip.ERROR: Failed building wheel for volcengine-python-sdkRunning setup.py clea…

java8 将对象list中的某一个属性取出组成一个list

实体类 public class Sp {String spdm;String spmc;public Sp() {}public Sp(String spdm, String spmc) {this.spdm spdm;this.spmc spmc;}public String getSpdm() {return spdm;}public void setSpdm(String spdm) {this.spdm spdm;}public String getSpmc() {return sp…

数据库讲解---(数据库设计)

目录 一.数据库设计概述 1.1数据库设计的内容 1.1.1数据库的结构设计 1.1.2数据库的行为设计 1.2数据库设计方法 1.2.1直观设计法 1.2.2规范设计法 1.2.3计算机辅助设计法 1.2.4自动化设计法 1.3数据库设计的基本步骤 1.3.1需求分析 1.3.2概念结构设计 1.3.3逻辑结…

气象站的种类和应用范围可以根据不同的分类标准进行详细的划分和描述

气象站的种类和应用范围可以根据不同的分类标准进行详细的划分和描述。以下是从不同角度对气象站的种类和应用范围的介绍&#xff1a; 一、气象站的种类 根据用途和安装环境分类&#xff1a; 农业气象站&#xff1a;专为农业生产服务&#xff0c;监测土壤温度、湿度等参数&am…

替代LTC3855双通道多相带差分遥测DC-DC同步控制器

特性:双通道、180 定相控制器降低了所需的输入电容和电源感应噪声高效率&#xff1a;达 95%RSENSE 或 DCR 电流检测可编程 DCR 温度补偿0.75%、0.6V 输出电压准确度可锁相固定频率&#xff1a;250kHz 至 770kHz真正的远端采样差分放大器双路 N 沟道 MOSFET 同步驱动宽 VIN 范围…

oracle 12c/19c OEM 无法访问怎么办?

作者介绍&#xff1a;老苏&#xff0c;10余年DBA工作运维经验&#xff0c;擅长Oracle、MySQL、PG数据库运维&#xff08;如安装迁移&#xff0c;性能优化、故障应急处理等&#xff09; 公众号&#xff1a;老苏畅谈运维 欢迎关注本人公众号&#xff0c;更多精彩与您分享。到了12…

网页中一些基本元素

1、页尾自适应 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style&g…

智慧校园-科研管理系统总体概述

在当前教育与科研深度融合的大潮中&#xff0c;智慧校园科研管理系统脱颖而出&#xff0c;它巧妙地融合了现代科技的力量&#xff0c;诸如云计算、大数据分析及人工智能技术&#xff0c;旨在为高等学府与科研机构打造一个高效运转、透明公开、促进协作的科研管理新生态。这一系…

0-30 VDC 稳压电源,电流控制 0.002-3 A

怎么运行的 首先&#xff0c;有一个次级绕组额定值为 24 V/3 A 的降压电源变压器&#xff0c;连接在电路输入点的引脚 1 和 2 上。&#xff08;电源输出的质量将直接影响与变压器的质量成正比&#xff09;。变压器次级绕组的交流电压经四个二极管D1-D4组成的电桥整流。桥输出端…

ETO MARKETS:美股涨势告急?通胀风暴或成市场新拐点

摘要&#xff1a; 近期美股涨势引发了市场对其可持续性的质疑。随着通胀数据的发布&#xff0c;全球利率前景可能面临新的变化。投资者需关注即将到来的通胀数据及其对市场的影响。本周的市场波动加剧&#xff0c;政治风险和经济数据共同作用&#xff0c;将为未来的市场走向提…

React 扩展

文章目录 PureComponent1. 使用 React.Component&#xff0c;不会进行浅比较2. 使用 shouldComponentUpdate 生命周期钩子&#xff0c;手动比较3. 使用 React.PureComponent&#xff0c;自动进行浅比较 Render Props1. 使用 Children props&#xff08;通过组件标签体传入结构&…

十大排序算法之->基数排序

一、计数排序简介 基数排序&#xff08;Radix Sort&#xff09;是一种非比较型整数排序算法&#xff0c;其原理是将整数按位数切割成不同的数字&#xff0c;然后按每个位数分别比较。具体做法是用0-9之间的所有整数作为键值&#xff0c;对数据集中的每一个数&#xff0c;按照从…

【方案】基于5G智慧工业园区解决方案(PPT原件)

5G智慧工业园区整体解决方案旨在通过集成5G通信技术、物联网、大数据和云计算等先进技术&#xff0c;实现园区的智能化、高效化和绿色化。 该方案首先构建高速、稳定的5G网络&#xff0c;确保园区内设备、人员与物流的实时连接和高效沟通。其次&#xff0c;通过工业物联网技术&…