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

news2024/11/16 6:43:16

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

首先,导入相关依赖:

<!--        引入切面依赖-->
        <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;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Around("@annotation(rateLimit)")
    public Object interceptor(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        String userid= redisTemplate.opsForValue().get("loginId");   //获取用户ID
        System.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.coloradmin.cn/o/1857833.html

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

相关文章

网络基础-协议

一、ARP 通过IP得到Mac 首先会查看缓存的arp表中是否有相应的IP和Mac对应关系&#xff0c;如果有直接进行包封装。如果没有则进行广播当对应的地址就收到广播包后会根据arp中的源地址进行单播返回相应的IP和Mac对应关系。 arp -a 查看现有的arp缓存 二、RARP反向地址解析 通过…

git上传本地项目及更新项目

1、注册GitHub账号和下载git 2、在GitHub上新建一个仓库&#xff0c;点击号——>New repository&#xff0c;给仓库起一个名字&#xff0c;点击Create repository 3、进入要上传的项目中&#xff0c;右键点击git back here&#xff0c;命令行输入git init初始化&#xff0c…

《Nest系列 - 3. 掌握常见Nest 装饰器,奠定坚实基础!!!!!!》

nest 一个核心就是依赖注入&#xff0c;而中的大部分功能都是通过装饰器来实现的&#xff0c;那什么是装饰器呢&#xff1f; 就是一个 xxx &#xff0c;诸如 Module&#xff0c;controller, Get, Post 那这样有什么好处呢&#xff1f; 可以把他理解成一个方法&#xff0c;在不改…

精密机械中的滚珠螺杆与螺杆支撑座的完美配合!

螺杆支撑座和滚珠螺杆是机械设备中的重要部件。滚珠螺杆通常运用在自动化设备中&#xff0c;需高速运动、高精度定位均依靠它的优良性能&#xff0c;如机床&#xff0c;数控、工业机器人等机械设备。螺杆支撑座装在螺杆的两端&#xff0c;支撑座有两端&#xff0c;固定端和支撑…

SpringBoot的迭代史,SpringBoot和Spring和Java和Maven和Gradle版本兼容介绍

文章目录 系统环境要求&#xff1a;Spring Boot 3.1.xSpring Boot 3.0.xSpring Boot 2.7.xSpring Boot 2.6.xSpring Boot 2.5.xSpring Boot 2.4.xSpring Boot 2.3.xSpring Boot 2.2.xSpring Boot 2.1.xSpring Boot 2.0.xSpring Boot 1.5.xSpring Boot 1.4.xSpring Boot 1.3.xSp…

代码随想录训练营Day 69|并查集理论基础、卡码网107.寻找存在的路径

1.并查集理论基础 并查集理论基础 | 代码随想录 并查集可以解决什么问题呢&#xff1f; 主要就是集合问题&#xff0c;两个节点在不在一个集合&#xff0c;也可以将两个节点添加到一个集合中。 注意&#xff1a;求根是求箭头出发的数 路径压缩&#xff1a;求根的根。把根的根的…

【投稿优惠|权威主办】2024年图像、地质测绘与遥感技术国际学术会议(ICIGSRST 2024)

【投稿优惠|权威主办】2024年图像、地质测绘与遥感技术国际学术会议&#xff08;ICIGSRST 2024&#xff09; 2024 International Conference on Image, Geological Surveying and Remote Sensing Technology&#xff08;ICIGSRST 2024&#xff09; ▶会议简介 2024年图像、地质…

大学生综合能力测评系统(安装+讲解+源码)

【毕设者】大学生综合能力测评系统(安装讲解源码) 分为管理员老师学生端 技术栈 后端: SpringBoot Mysql MybatisPlus 前端: Vue Element 功能截图: 给你安装运行

图片在线加水印工具,快速将图片铺满水印

有些同学为了防止图片未经授权的使用和传播&#xff0c;想要将图片添加铺满水印&#xff0c;但是不知道如何操作。下面小编就来和大家分享如何使用图片在线加水印工具&#xff0c;快速的将图片铺满水印。 有许多在线工具可以帮助我们快速、高效地给图片添加水印。在线添加水印&…

ONLYOFFICE8.1版本桌面编辑器的测评

首先我们先出示一下我们所测评官网的链接&#xff1a; ONLYOFFICE官网链接&#xff1a;ONLYOFFICE - 企业在线办公应用软件 | ONLYOFFICE 一进来就是这么简单整洁的页面&#xff0c;让人看了之后清新开朗&#xff0c;目标明确。 我们这款onlyoffice有着众多优点&#xff0c;如…

HTML5休闲小游戏《猫猫超市》源码,引流、刷广告利器

HTML5休闲小游戏《猫猫超市》源码&#xff0c;直接把源码上传到服务器就能使用了&#xff01; 下载链接&#xff1a;https://www.huzhan.com/code/goods467910.html

VMware虚拟机卡顿(虚拟机卡死)(调整所有虚拟机内存使其适应预留的主机 RAM (F)、默认进程优先级、不允许使用内存页面修整功能(M))

文章目录 设置编辑——首选项——内存——额外内存——调整所有虚拟机内存使其适应预留的主机 RAM (F)&#xff08;我把这个勾上了&#xff09;编辑——首选项——优先级——默认进程优先级虚拟机——设置——选项——高级——不允许使用内存页面修整功能(M) 参考文章&#xff…

Perplexily首席执行官Aravind Srinivas斯里尼·瓦斯回应抄袭和侵权指控

Perplexity的“答案引擎”工作原理是收集网络上的海量信息&#xff0c;构建一个庞大的内容数据库&#xff08;索引&#xff09;。用户无需输入关键字&#xff0c;只需在Perplexity的平台或应用中提问&#xff0c;就能获得包含引文及网络内容链接的详细回答。 网站通过“机器人…

线性代数|机器学习-P18快速下降奇异值

文章目录 1. 为什么要低秩矩阵 1. 为什么要低秩矩阵 我们的世界里面有很多数据&#xff0c;如果我们原封不动的发送数据&#xff0c;那么会导致数据量的增大&#xff0c;我们希望对数据进行压缩后再打包压缩&#xff0c;这样的话我们能够在带宽一定的情况下发送更多的数据&…

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

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

Snowflake 外部表的最新增强功能:您需要了解的内容

自从我们上一篇关于如何使用 MinIO 扩展 Snowflake 实现的博客文章以来&#xff0c;Snowflake 对外部表的支持已经发生了重大更新。外部表允许 Snowflake 用户将对象存储&#xff08;如 MinIO&#xff09;中的数据视为 Snowflake 中的只读表&#xff0c;而无需迁移。Snowflake …

制造业工厂急需一套erp系统帮他降本增效

随着全球制造业竞争的日益激烈&#xff0c;制造业工厂面临着越来越多的挑战&#xff0c;包括成本控制、生产效率、市场响应速度等方面。在这样的背景下&#xff0c;一套高效的ERP&#xff08;企业资源规划&#xff09;系统成为了制造业工厂降本增效的必备工具。本文将探讨ERP系…

视频融合共享平台LntonCVS视频监控平台在农场果园等场景的使用方案

我国大江南北遍布着各类果园。传统的安全防范方式主要是建立围墙&#xff0c;但这种方式难以彻底阻挡不法分子的入侵和破坏。因此&#xff0c;需要一套先进、科学、实用且稳定的安全防范报警系统&#xff0c;以及时发现并处理潜在问题。 需求分析 由于果园地处偏远且缺乏有效防…

【每日刷题】Day74

【每日刷题】Day74 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 1394. 找出数组中的幸运数 - 力扣&#xff08;LeetCode&#xff09; 2. 1518. 换水问题 - 力扣&…

solidworks安装教程 - 解决安装后服务不能自动启动问题

Solidworks安装教程&#xff0c;有些同学的电脑过于复杂&#xff0c;产生了正常的服务不能启动。 前面的有个重要的操作操作界面有&#xff0c;大家应该是执行了&#xff1a; 那么我们有变通的方法可以让这个服务启动&#xff1a; 1. cmd用管理员启动 2. 测试下如下命令是否…