基于redis实现API接口访问次数限制

一,概述
日常开发中会有一个常见的需求,需要限制接口在单位时间内的访问次数,比如说某个免费的接口限制单个IP一分钟内只能访问5次。该怎么实现呢,通常大家都会想到用redis,确实通过redis可以实现这个功能,下面实现一下。
二,常见错误
固定时间窗口
有人设计了一个在每分钟内只允许访问1000次的限流方案,如下图01:00s-02:00s之间只允许访问1000次。这种设计的问题在于,请求可能在01:59s-02:00s之间被请求1000次,02:00s-02:01s之间被请求了1000次,这种情况下01:59s-02:01s间隔0.02s之间被请求2000次,很显然这种设计是错误的。

三, 实现
1,基于滑动时间窗口

在指定的时间窗口内次数是累积的,超过阈值,都会限制。
2,流程如下

3,代码实现
前提:pom文件引入redis,Spring AOP等
(1)添加注解RequestLimit
package com.xxx.demo.aspect;import java.lang.annotation.*;/*** 接口访问频率注解,默认一分钟只能访问10次*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {// 限制时间 单位:秒(默认值:一分钟)long period() default 60;// 允许请求的次数(默认值:10次)long count() default 10;
}
(2)添加切面实现注解的限制访问逻辑
package com.xxx.demo.aspect;import com.xgd.demo.commons.ErrorCode;
import com.xgd.demo.handler.BusinessException;
import com.xgd.demo.util.IpUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.concurrent.TimeUnit;/*** @date 2024/11/8 上午8:43*/
@Aspect
@Component
@Log4j2
public class RequestLimitAspect {@AutowiredRedisTemplate redisTemplate;@Pointcut("@annotation(requestLimit)")public void controllerAspect(RequestLimit requestLimit) {}@Around("controllerAspect(requestLimit)")public Object doAround(ProceedingJoinPoint joinPoint, RequestLimit requestLimit) throws Throwable {// 从注解中获取限制次数和窗口时间long period = requestLimit.period();long limitCount = requestLimit.count();// 请求ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();assert attributes != null;HttpServletRequest request = attributes.getRequest();String ip = IpUtil.getIpFromRequest(request);String uri = request.getRequestURI();//设置客户端访问的keyString key = "req_limit_".concat(uri).concat(ip);ZSetOperations zSetOperations = redisTemplate.opsForZSet();// 添加当前时间戳,分数为当前时间戳long currentMs = System.currentTimeMillis();zSetOperations.add(key, currentMs, currentMs);// 设置窗口时间作为过期时间redisTemplate.expire(key, period, TimeUnit.SECONDS);// 移除掉不在窗口里的数据zSetOperations.removeRangeByScore(key, 0, currentMs - period * 1000);// 查询窗口内已经访问过的次数Long count = zSetOperations.zCard(key);if (count > limitCount) {log.error("接口拦截:{} 请求超过限制频率【{}次/{}s】,IP为{}", uri, limitCount, period, ip);throw new BusinessException(ErrorCode.REQUEST_LIMITED.getCode(), ErrorCode.REQUEST_LIMITED.getMessage());}// 继续执行请求return joinPoint.proceed();}
}
上面里面请求被拦截,是抛出了一个自定义的业务异常,大家可以根据自己的情况自己定义。
(3)同时附上上面中引用到自定义工具类
package com.xxx.demo.util;import jakarta.servlet.http.HttpServletRequest;
import java.util.Objects;/*** @date 2024/11/8 上午9:06*/
public class IpUtil {private static final String X_FORWARDED_FOR_HEADER = "X-Forwarded-For";private static final String X_REAL_IP_HEADER = "X-Real-IP";/*** 从请求中获取IP** @return IP;当获取不到时,返回null*/public static String getIpFromRequest(HttpServletRequest request ) {return getRealIp(request);}/*** 获取请求的真实IP,优先级从高到低为:<br/>* 1.从请求头X-Forwarded-For中获取ip,并且只获取第一个ip(从左到右) <br/>* 2.从请求头X-Real-IP中获取ip <br/>* 3.使用{@link HttpServletRequest#getRemoteAddr()}方法获取ip** @param request 请求对象,必须不能为null* @return ip*/private static String getRealIp(HttpServletRequest request) {Objects.requireNonNull(request, "request must be not null");String ip = request.getHeader(X_FORWARDED_FOR_HEADER);if (ip != null && !ip.isBlank()) {int delimiterIndex = ip.indexOf(',');if (delimiterIndex != -1) {// 如果存在多个ip,则取第一个ipip = ip.substring(0, delimiterIndex);}return ip;}ip = request.getHeader(X_REAL_IP_HEADER);if (ip != null && !ip.isBlank()) {return ip;} else {return request.getRemoteAddr();}}
}
(4)使用注解
这里限制为10秒内只允许访问3次,超过就抛出异常

(5)访问测试
前3次访问,接口正常访问

后面的访问,返回自定义异常的结果

如果对你有帮助,记得点赞关注哟!
相关文章:
基于redis实现API接口访问次数限制
一,概述 日常开发中会有一个常见的需求,需要限制接口在单位时间内的访问次数,比如说某个免费的接口限制单个IP一分钟内只能访问5次。该怎么实现呢,通常大家都会想到用redis,确实通过redis可以实现这个功能,…...
[ Linux 命令基础 3 ] Linux 命令详解-文件和目录管理命令
🍬 博主介绍 👨🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…...
npm i 的时候报错: npm ERR! Error: EPERM: operation not permitted, rename
文章目录 噩梦解决办法总结 噩梦 最近改漏洞,这个项目删掉了 node_modules文件夹 重新安装依赖,结果安装一半的时候就一直报这个错。 然后查了很多方法,基本都是下面这些: 权限不够,以管理员运行cmd重新安装。清除 n…...
如何迁移剪映源文件
1、打开剪映,打开全局设置 2、查看草稿位置。把要迁移的文件拷贝到这个路径下面。 3、关闭文件,返回上一层界面,可以看到拷贝到目录下的文件。...
Go语言中的`io.Copy`函数:高效的数据复制解决方案
在Go语言中,io.Copy函数是一个强大而高效的工具,用于将数据从一个io.Reader复制到一个io.Writer。这篇文章将深入探讨io.Copy函数的工作原理、使用方法及其在实际应用中的优势。无论您是后端开发人员还是对Go语言感兴趣的程序员,这篇文章都将…...
datastage在升级版本到11.7之后,部分在11.3上正常执行的SP报错SQLSTATE = 22007: 本机错误代码 = -180
在升级版本到11.7之后,部分在11.3上正常执行的SP开始报错,报的SQL错误是时间参数问题,但是一样的SP可以直接call sp执行,也可以手动调用作业执行,只有设置定时调度时作业会报错, CALLXXX.XXX(1,CURRENT TIM…...
docker——项目部署
什么是Docker? Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可抑制的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器完全使用沙盒机制,相互之间不会存在任何接口。几…...
设计模式(Unity)——更新中
设计模式 文章目录 设计模式工厂模式创建方法(Create Methods)简单工厂(Simple Factory)工厂方法(Method Factory)抽象工厂(Abstract Factroy) 策略模式 工厂模式 创建方法…...
小程序中引入下载到本地的iconfont字体图标加载不出来问题解决
我这个是uniapp项目,字体图标都是一样的,在vue项目中web端、uniapp运行到h5都没问题,但是运行到小程序加载不出来,报错如下: 不让用本地路径,所以我们要转为base64编码,这里给大家提供一个工具,它可以把本地字体文件转为base64:transfonter 进入官网后,第一步: …...
百度富文本禁止编辑
<script type"text/javascript">$(function () {editorcontent new baidu.editor.ui.Editor();editorcontent.render(authentication);//禁用代码editorcontent.ready(function () {editorcontent.setDisabled();});try {editorcontent.sync();} catch (err) …...
C++开发基础之使用librabbitmq库实现RabbitMQ消息队列通信
1. 前言 RabbitMQ是一个流行的开源消息队列系统,支持多种消息协议,广泛用于构建分布式系统和微服务架构。可以在不同应用程序之间实现异步消息传递。在本文中,我们将熟悉如何使用C与RabbitMQ进行消息通信。 2. 准备工作 在 Windows 平台上…...
头歌网络安全(11.12)
头歌禁止复制解决 必须先下篡改猴!!!! 头歌复制助手 Educoder Copy Helperhttps://scriptcat.org/zh-CN/script-show-page/1860 Java生成验证码 第1关:使用Servlet生成验证码 任务描述 本关任务:使用se…...
洛谷 P1725 琪露诺(线段树优化dp)
题目链接 https://www.luogu.com.cn/problem/P1725 思路 我们令 d p [ i ] dp[i] dp[i]表示琪露诺移动到第 i i i个格子时能够获得的最大冰冻指数。 显然,状态转移方程为: d p [ i ] m a x ( d p [ i ] , d p [ k ] a [ i ] ) dp[i] max(dp[i],dp…...
【LeetCode】【算法】19. 删除链表的倒数第N个结点
LeetCode 19. 删除链表的倒数第N个结点 题目描述 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 思路 思路:快慢指针,快指针先移动n步,快慢指针再同时移动直到快指针到达链表末尾,此…...
Python爬虫 | 爬取豆瓣电影Top250的数据
简单记录一下,实现爬取豆瓣电影Top 250的数据。 这里我使用requests库来发送HTTP请求,以及BeautifulSoup库来解析HTML页面。 1.安装requests和BeautifulSoup库。 如果没有安装,可以通过以下命令安装: pip install requests bea…...
mac 中python 安装mysqlclient 出现 ld: library ‘ssl‘ not found错误
1. 出现报错 2. 获取openssl位置 brew info openssl 3. 配置环境变量(我的是在~/.bash.profile) export LDFLAGS"-L/opt/homebrew/Cellar/openssl3/3.4.0/lib" export CPPFLAGS"-I/opt/homebrew/Cellar/openssl3/…...
完全清除:苹果手机照片怎么彻底删除
在使用iPhone的过程中,由于拍摄积累的照片往往会占用大量存储空间。有时候,我们需要彻底删除这些照片以释放空间或保护隐私。苹果手机照片怎么彻底删除?在此,本文将与你分享一些实用的技巧。 彻底删除的重要性 彻底删除照片不仅涉…...
高德地图多个图片组成标点(自定义点标记内容)
图标的实现自定义点标记内容...
02-1_MVCC版本链清理
MVCC-版本链清理 文章目录 MVCC-版本链清理简介依赖机制Purge 操作的触发时机版本链清理的详细过程示例操作流程延迟清理配置和监控总结 简介 MySQL 中的 MVCC 机制通过版本链来管理数据的多版本存储,以支持高并发的读写操作。然而,随着事务的进行&…...
探索Python视频处理的瑞士军刀:ffmpeg-python库
文章目录 **探索Python视频处理的瑞士军刀:ffmpeg-python库**第一部分:背景介绍第二部分:ffmpeg-python库是什么?第三部分:如何安装ffmpeg-python库?第四部分:简单库函数使用方法1. 视频转码2. …...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
基于单片机的宠物屋智能系统设计与实现(论文+源码)
本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢,连接红外测温传感器,可实时精准捕捉宠物体温变化,以便及时发现健康异常;水位检测传感器时刻监测饮用水余量,防止宠物…...
HTTPS证书一年多少钱?
HTTPS证书作为保障网站数据传输安全的重要工具,成为众多网站运营者的必备选择。然而,面对市场上种类繁多的HTTPS证书,其一年费用究竟是多少,又受哪些因素影响呢? 首先,HTTPS证书通常在PinTrust这样的专业平…...
未授权访问事件频发,我们应当如何应对?
在当下,数据已成为企业和组织的核心资产,是推动业务发展、决策制定以及创新的关键驱动力。然而,未授权访问这一隐匿的安全威胁,正如同高悬的达摩克利斯之剑,时刻威胁着数据的安全,一旦触发,便可…...
触发DMA传输错误中断问题排查
在STM32项目中,集成BLE模块后触发DMA传输错误中断(DMA2_Stream1_IRQHandler进入错误流程),但单独运行BLE模块时正常,表明问题可能源于原有线程与BLE模块的交互冲突。以下是逐步排查与解决方案: 一、问题根源…...
详解鸿蒙Next仓颉开发语言中的动画
大家上午好,今天来聊一聊仓颉开发语言中的动画开发。 仓颉中的动画通常有两种方式,分别是属性动画和显示动画,我们今天以下面的加载动画为例,使用显示动画和属性动画分别实现一下,看看他们有什么区别。 显示动画 显示…...
