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

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时间内的最大请求数

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

相关文章:

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

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

ravynOS 0.5.0 发布 - 基于 FreeBSD 的 macOS 兼容开源操作系统

ravynOS 0.5.0 发布 - 基于 FreeBSD 的 macOS 兼容开源操作系统 ravynOS - 一个旨在提供 macOS 的精致性和 FreeBSD 的自由度的操作系统 请访问原文链接&#xff1a;https://sysin.org/blog/ravynos/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页…...

韩国面临的本地化挑战

西方文化&#xff0c;尤其是美国电影、音乐和游戏&#xff0c;对韩国也产生了同样大的影响。众所周知&#xff0c;这个国家与外国产品的关系更加开放。然而&#xff0c;游戏在这里仍然受到审查&#xff0c;所以最好避免与朝鲜、日本等有关的分裂性政治主题。否则&#xff0c;你…...

Linux内存从0到1学习笔记(8.17 SMMU Fault调试方法)

写在前面 通过前面的介绍,我们知道了SMMU实际上是一个针对外设的MMU。它作为一个硬件IP被设备执行DMA操作时使用。 再来简单回顾下SMMU的工作流程: 外设 ---> DMA操作 ---> SMMU ---> Memory 也就是说,通常情况下驱动程序会先分配DMA Buffer ---> 然后执行S…...

讲座学习截图——《CAD/CAE/CAM几何引擎-软件概述》(一)

目录 引出CAD/CAE/CAM几何引擎-软件概述 郝建兵CADCAECAM 几何模型内核ACIS 两个老大之一Open CascadeParasolid 两个老大之一Autodesk的内核 总结其他自定义信号和槽1.自定义信号2.自定义槽3.建立连接4.进行触发 自定义信号重载带参数的按钮触发信号触发信号拓展 lambda表达式…...

鸿蒙开发系统基础能力:【@ohos.hichecker (检测模式)】

检测模式 HiChecker可以作为应用开发阶段使用的检测工具&#xff0c;用于检测代码运行过程中部分易忽略的问题&#xff0c;如应用线程出现耗时调用、应用进程中Ability资源泄露等问题。开发者可以通过日志记录或进程crash等形式查看具体问题并进行修改&#xff0c;提升应用的使…...

WordPress CDN是什么?CDN有什么作用?

您想让您的网站加载速度更快吗&#xff1f; 网站所有者希望网站加载速度快&#xff0c;内容丰富&#xff0c;功能强大&#xff0c;吸引用户。然而&#xff0c;添加这些功能可能会降低网站速度&#xff0c;难以快速向全球用户提供内容。 这就是为什么许多WordPress网站使用 CDN…...

【containerd】Containerd高阶命令行工具nerdctl

前言 对于习惯了使用docker cli的用户来说&#xff0c;containerd的命令行工具ctr使用起来不是很顺手&#xff0c;此时别慌&#xff0c;还有另外一个命令行工具项目nerdctl可供我们选择。 nerdctl是一个与docker cli风格兼容的containerd的cli工具。 nerdctl已经作为子项目加入…...

Spring+SpringMVC+MyBatis整合

目录 1.SSM介绍1.1 什么是SSM&#xff1f;1.2 SSM框架1.2.1 Spring1.2.2 SpringMVC1.2.3 MyBatis 2.SSM框架整合2.1 建库建表2.2 创建工程2.3 pom.xml2.4 log4j.properties2.5 db.properties2.6 applicationContext-dao.xml2.7.applicationContext-tx.xml2.8 applicationContex…...

springboot+vue+mybatis穷游管理系统+PPT+论文+讲解+售后

随着现在网络的快速发展&#xff0c;网上管理系统也逐渐快速发展起来&#xff0c;网上管理模式很快融入到了许多企业的之中&#xff0c;随之就产生了“基于vue的穷游管理系统”&#xff0c;这样就让基于vue的穷游管理系统更加方便简单。 对于本基于vue的穷游管理系统的设计来说…...

ClickHouse备份方案

ClickHouse备份方案主要包括以下几种方法&#xff1a; 一、使用clickhouse-backup工具&#xff1a; &#xff08;参考地址&#xff1a;https://blog.csdn.net/qq_43510111/article/details/136570850&#xff09; **安装与配置&#xff1a;**首先从GitHub获取clickhouse-bac…...

windows启用和禁用内存压缩

windows内存压缩 Windows操作系统的内存压缩是一种通过压缩和解压缩内存页面来减少内存使用量的技术。当系统的内存使用达到一定阈值时&#xff0c;Windows会将不常用的内存页面压缩为一个稳定的压缩文件&#xff0c;以释放更多的内存空间。 内存压缩的主要目的是减少页面交换…...

MATLAB-振动问题:单自由度无阻尼振动系统受迫振动

一、基本理论 二、MATLAB实现 令式&#xff08;1.3&#xff09;中A0 2&#xff0c;omega0 30&#xff0c;omega 40&#xff0c;matlab程序如下&#xff1a; clear; clc; close all;A0 2; omega0 30; omega 40; t 0:0.02:5; y A0 * sin( (omega0 - omega) * t /2) .* s…...

示例:WPF中应用DependencyPropertyDescriptor监视依赖属性值的改变

一、目的&#xff1a;开发过程中&#xff0c;经常碰到使用别人的控件时有些属性改变没有对应的事件抛出&#xff0c;从而无法做处理。比如TextBlock当修改了IsEnabled属性我们可以用IsEnabledChanged事件去做对应的逻辑处理&#xff0c;那么如果有类似Background属性改变我想找…...

链家房屋数据爬取与预处理-大数据采集与预处理课程设计

芜湖市链家二手房可视化平台 成品展示 重点说明 1.数据特征数量和名称、数据量 数据特征数量&#xff1a;14&#xff1b; 名称&#xff1a;小区名、价格/万、地区、房屋户型、所在楼层、建筑面积/平方米、户型结构、套内面积、建筑类型、房屋朝向、建筑结构、装修情况、梯户…...

一种502 bad gateway nginx/1.18.0的解决办法

背景:上线的服务突然挂掉了 step1&#xff0c;去后端日志查看&#xff0c;发现并无异常&#xff0c;就是请求无法被接收 step2&#xff0c;查看了nginx的错误日志&#xff0c;发现该文件为空 step3&#xff0c;查看了niginx的运行日志&#xff0c;发现了以下问题 [error] 38#…...

二叉树第一期:树与二叉树的概念

一、树 1.树的定义 与线性表不同&#xff0c;树是一种非线性的数据结构&#xff0c;由N(N>0)个结点组成的具有层次关系的集合&#xff1b;因其形状类似生活中一颗倒挂着的树&#xff0c;故将其数据结构称为树。 2.树的相关概念 根结点 没有前驱的结点&#xff0c;称为根…...

vue跨域问题,请注意你的项目是vue2还是vue3

uniapp跨域设置了&#xff0c;但还是有问题 uniapp设置代理后还是无法请求后端接口vue2项目设置代理vue3项目设置代理 uniapp设置代理后还是无法请求后端接口 如果你在possman&#xff0c;apifox上测试接口都没有问题&#xff0c;但是在hbuild项目中设置代理后&#xff0c;还是…...

大厂晋升学习方法一:海绵学习法

早晨 30 分钟 首先&#xff0c;我们可以把起床的闹钟提前 30 分钟&#xff0c;比如原来 07:30 的闹钟可以改为 07:00。不用担心提前 30 分钟起床会影响休息质量&#xff0c;习惯以后&#xff0c;早起 30 分钟不但不会影响一天的精力&#xff0c;甚至可能反而让人更有精神。早起…...

【ARMv8/v9 GIC 系列 4.2 -- GIC CPU Interface 详细介绍】

文章目录 GIC CPU Interface 介绍CPU Interface 主要寄存器 GIC CPU Interface 介绍 A 系列处理器提供 5个管脚来实现中断&#xff0c;分别是&#xff1a; nIRQ&#xff1a;物理普通中断nFIQ&#xff1a;物理快速中断nVIRQ&#xff1a;虚拟普通中断nVFIQ&#xff1a;虚拟快速…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...