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.rhkb.cn/news/364380.html

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

相关文章

[数据集][目标检测]电力场景下电柜箱门把手检测数据集VOC+YOLO格式1167张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1167 标注数量(xml文件个数)&#xff1a;1167 标注数量(txt文件个数)&#xff1a;1167 标注…

操作系统(OS)

1.1.1操作系统的概念&#xff08;定义&#xff09; 操作系统&#xff08;Operation System&#xff0c;OS&#xff09;是指控制和管理整个计算机系统的硬件和软件资源&#xff0c;并合理地组织调度计算机的工作和资源的分配&#xff1b;&#xff08;操作系统是系统资源的管理者…

苹果电脑插上移动硬盘没反应怎么回事 苹果笔记本移动硬盘无法写入 苹果电脑读取不到移动硬盘数据怎么办 移动硬盘连接苹果电脑无法读取的解决方案

通常情况下&#xff0c;当我们把硬盘接到苹果电脑上&#xff0c;我们是可以读取它的。但也有用户遇到过这种情况&#xff0c;就是苹果电脑无法正常读取硬盘&#xff0c;这是怎么造成的呢&#xff1f; 一、硬盘在苹果电脑上读取不出来的原因是什么&#xff1f; 硬盘在苹果电脑上…

Golang | Leetcode Golang题解之第198题打家劫舍

题目&#xff1a; 题解&#xff1a; func rob(nums []int) int {if len(nums) 0 {return 0}if len(nums) 1 {return nums[0]}first : nums[0]second : max(nums[0], nums[1])for i : 2; i < len(nums); i {first, second second, max(first nums[i], second)}return se…

FPGA SATA高速存储设计

今天来讲一篇如何在fpga上实现sata ip&#xff0c;然后利用sata ip实现读写sata 盘的目的&#xff0c;如果需要再速度和容量上增加&#xff0c;那么仅仅需要增加sata ip个数就能够实现增加sata盘&#xff0c;如果仅仅实现data的读写整体来说sata ip设计比较简单&#xff0c;下面…

T4打卡 学习笔记

所用环境 ● 语言环境&#xff1a;Python3.11 ● 编译器&#xff1a;jupyter notebook ● 深度学习框架&#xff1a;TensorFlow2.16.1 ● 显卡&#xff08;GPU&#xff09;&#xff1a;NVIDIA GeForce RTX 2070 设置GPU from tensorflow import keras from tensorflow.keras…

使用Vercel 搭建自己的Dashy导航页

背景 Dashy 是一个开源的自托管导航页面配置服务&#xff0c;它具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。用户可以利用 Dashy 将自己常用的一些网站聚合起来&#xff0c;形成一个个性化的导航页面。 同类的竞品还有Heimdall, Flare 等。 可以通过Docker 等…

selenium4如何指定chrome和firefox的驱动(driver)路径

pythonpytestselenium框架的自动化测试脚本。 原本用的chrome&#xff0c;很久没用了&#xff0c;今天执行&#xff0c;发现chrome偷偷升级&#xff0c;我的chromedriver版本不对了。。。鉴于访问chrome相关网站太艰难&#xff0c;决定弃用chrome&#xff0c;改用firefox。因为…

用易查分制作《假期安全承诺书》支持在线手写签名,一键导出打印

暑假将至&#xff0c;学校通常会下发假期安全承诺书让家长签署。 易查分可以实现网上下发安全承诺书通知&#xff0c;让学生家长进行签名确认&#xff0c;还可以导出PDF文件&#xff0c;方便打印一人一张的纸质版承诺书&#xff0c;下面就来教给大家如何使用吧&#xff01; 暑假…

【语言模型】Xinference的部署过程

一、引言 Xinference&#xff0c;也称为Xorbits Inference&#xff0c;是一个性能强大且功能全面的分布式推理框架&#xff0c;专为各种模型的推理而设计。无论是研究者、开发者还是数据科学家&#xff0c;都可以通过Xinference轻松部署自己的模型或内置的前沿开源模型。Xinfe…

第三十七篇——麦克斯韦的妖:为什么要保持系统的开放性?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 如果没有详细的学习这篇文章&#xff0c;我觉得我就是被麦克斯韦妖摆弄的…

[OtterCTF 2018]Graphic‘s For The Weak

恶意软件的图形中有些可疑之处。 软件图形 &#xff1f;&#xff1f;&#xff1f;这里的恶意文件都是 vmware-tray.ex使用procdump转存进程的可执行文件 &#xff08;可执行的&#xff09;导出了 &#xff0c;看文件里面是否存在 图片 volatility.exe -f .\OtterCTF.vmem --pro…

长鑫存储母公司斥资24亿美元发展国产HBM

国产DRAM厂商长鑫存储母公司睿力集成计划投资24亿美元在上海建一座高端封装工厂。据报道&#xff0c;该工厂将专注于高带宽存储器&#xff08;HBM&#xff09;芯片的封装&#xff0c;预计到2026年中开始投入生产。长鑫存储将利用来自多方投资者的资金进行建设&#xff0c;其中包…

CXL:拯救NVMe SSD缓存不足设计难题-2

LMB提出了基于CXL协议的内存扩展框架和内核模块。该方案利用CXL内存扩展器作为物理DRAM源&#xff0c;旨在提供一个统一的内存分配接口&#xff0c;使PCIe和CXL设备都能方便地访问扩展的内存资源。通过这个接口&#xff0c;NVMe驱动和CUDA的统一内存内核驱动可以直接高效地访问…

1-爬虫基础知识(6节课学会爬虫)

1-爬虫基础知识&#xff08;6节课学会爬虫&#xff09; 1.什么是爬虫2.爬取的数据去哪了3.需要的软件和环境4.浏览器的请求&#xff08;1&#xff09;Url&#xff08;2&#xff09;浏览器请求url地址&#xff08;3&#xff09;url地址对应的响应 5.认识HTTP/HTTPS5.1 http协议之…

如何应对UI测试自动化的不稳定循环!

以下为作者观点&#xff1a; 当我加入UI自动化团队时&#xff0c;我很高兴能为新功能的自动化测试用例开发做出贡献。然而&#xff0c;我很快意识到团队花费了大量时间来修复之前迭代中不稳定的测试。这种情况让我感到困惑&#xff0c;因为当自动化测试脚本已知不稳定时&#…

Excel+vue+java实现批量处理功能

需求背景: 产品创建流程比较复杂&#xff0c;有时候需要一次性创建多至10个&#xff0c;所以做了Excel维护产品信息&#xff0c;直接导入创建的功能。能极大提高效率。 简要概括实现&#xff1a; 一、参考单个创建&#xff0c;设计创建模板&#xff0c;表头对应填写字段名&…

《昇思25天学习打卡营第12天 | 昇思MindSpore基于MindSpore的GPT2文本摘要》

12天 本节学习了基于MindSpore的GPT2文本摘要。 1.数据集加载与处理 1.1.数据集加载 1.2.数据预处理 2.模型构建 2.1构建GPT2ForSummarization模型 2.2动态学习率 3.模型训练 4.模型推理

javaScript利用indexOf()查找字符串的某个字符出现的位置

1 创建字符串 2 利用indexof()查询字符串的字符 3 利用while循环判断indexOf是否等于-1&#xff0c;不等于-1就打印一次并且索引号1去查下一个字符 //创建字符串var str1234567812311231;var indexstr.indexOf(1);//查询该字符while(index !-1)//indexOf()没有查到会返回-1{…

【linux/shell案例实战】解决Linux和Windows的换行符CRLF和LF问题

目录 一.什么是Linux 和 Windows 的换行符 CRLF 和 LF 二.使用Linux 中命令 dos2unix 和 unix2dos 实现CRLF 和LF的转换 三.使用 windows 中的代码编辑器实现 CRLF 和 LF 的转换&#xff08;Notepad&#xff09; 一.什么是Linux 和 Windows 的换行符 CRLF 和 LF CR是Carria…