Spring Boot+Redis 实现一个简单的限流器示例

Spring Boot+Redis 实现一个简单的限流器,限制

文章目录

  • Spring Boot+Redis 实现一个简单的限流器,限制
  • 0.前言
  • 1.基础介绍
  • 2.步骤
    • 2.1. 引入依赖
    • 2.2. 配置文件
    • 2.3. 核心源码
    • 优化后
    • 再优化一下加入布隆过滤器
  • 4.总结
  • 5.参考文档
  • 6. Redis从入门到精通系列文章

在这里插入图片描述

0.前言

在Spring Boot中使用Redis和过滤器实现请求限流。过滤器将在每个请求到达时检查请求频率,并根据设定的阈值进行限制。这样可以保护您的应用程序免受恶意请求或高并发请求的影响。请根据您的具体需求和业务场景进行适当的修改和扩展。

1.基础介绍

1. 限流场景
假设我们有一个API接口,需要限制每个用户在一段时间内的请求频率。比如每秒只允许请求100次等等的业务需求。
2. 实现限流逻辑:
使用Redis的计数器功能可以实现基于时间窗口的限流算法。通过在Redis中存储请求计数器和过期时间,可以控制单位时间内的请求频率。在需要进行限流的接口或方法中,使用Redis的原子操作(如INCR和EXPIRE)来增加计数器并设置过期时间。
在每个请求到达时,检查计数器的值是否超过设定的阈值,如果超过则拒绝请求,否则允许请求继续执行。

本文我们通过Spring Boot +Redis 实现一个轻量级的消息队列。

2.步骤

2.1. 引入依赖

<dependencies><!-- Spring Data Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
</dependencies>

2.2. 配置文件

# Redis连接配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=your_password
spring.redis.database=0# Redis连接池配置
spring.redis.jedis.pool.max-active=50
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=5
spring.redis.jedis.pool.max-wait=-1

在上面的配置中,您可以根据实际情况修改以下属性:

  • spring.redis.host:Redis服务器的主机名或IP地址。
  • spring.redis.port:Redis服务器的端口号。
  • spring.redis.password:Redis服务器的密码(如果有的话)。
  • spring.redis.database:Redis数据库的索引,默认为0。

另外,您还可以配置Redis连接池的属性,以控制连接池的行为。在示例配置中,设置了以下连接池属性:

  • spring.redis.jedis.pool.max-active:连接池中的最大活动连接数。
  • spring.redis.jedis.pool.max-idle:连接池中的最大空闲连接数。
  • spring.redis.jedis.pool.min-idle:连接池中的最小空闲连接数。
  • spring.redis.jedis.pool.max-wait:从连接池获取连接的最大等待时间(毫秒),-1表示无限等待。

如果 使用的是YAML格式的配置文件(application.yml),可以将上述配置转换为相应的格式:

spring:redis:host: 127.0.0.1port: 6379password: your_passworddatabase: 0redis.jedis.pool:max-active: 50max-idle: 10min-idle: 5max-wait: -1

请根据您的实际Redis服务器配置进行调整,并根据需要添加其他相关配置,如超时设置、SSL配置等。

2.3. 核心源码

  1. 实现请求限流过滤器
    创建一个实现javax.servlet.Filter接口的请求限流过滤器。在过滤器中,使用Redis的计数器功能来实现请求限流逻辑。
    示例中,RequestLimitFilter是一个实现了javax.servlet.Filter接口的请求限流过滤器。它使用Redis的计数器功能来实现请求限流逻辑。每个请求到达时,根据客户端的IP地址作为Redis的键,增加计数器的值并设置过期时间为指定的时间窗口。如果计数器超过了设定的阈值(这里是100),则返回HTTP 429 Too Many Requests响应。

示例中使用的是RedisTemplate<String, String>来操作Redis, 可以根据需要调整为适合您的数据类型和操作方式的RedisTemplate。

@Component
public class RequestLimitFilter implements Filter {@Autowiredprivate RedisTemplate<String, String> redisTemplate;private static final String REQUEST_LIMIT_PREFIX = "requestLimit:";private static final long REQUEST_LIMIT = 100; // 请求限制数量private static final long TIME_WINDOW = 60; // 时间窗口(单位:秒)@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;String ipAddress = getClientIpAddress(httpRequest);String key = REQUEST_LIMIT_PREFIX + ipAddress;Long counter = redisTemplate.opsForValue().increment(key, 1);if (counter == 1) {redisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS);}if (counter > REQUEST_LIMIT) {HttpServletResponse httpResponse = (HttpServletResponse) response;httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());httpResponse.getWriter().write("请求频率超过限制,请稍后再试!");return;}chain.doFilter(request, response);}private String getClientIpAddress(HttpServletRequest request) {String ipAddress = request.getHeader("X-Forwarded-For");if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("Proxy-Client-IP");}if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("WL-Proxy-Client-IP");}if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddr();}return ipAddress;
}
}

优化后

public class RequestLimitFilter implements Filter {@Autowiredprivate RedisTemplate<String, String> redisTemplate;private static final String REQUEST_LIMIT_PREFIX = "requestLimit:";private static final long REQUEST_LIMIT = 100; // 请求限制数量private static final long TIME_WINDOW = 60; // 时间窗口(单位:秒)@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;String ipAddress = getClientIpAddress(httpRequest);String key = REQUEST_LIMIT_PREFIX + ipAddress;Long counter = redisTemplate.opsForValue().increment(key, 1);if (counter == 1) {redisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS);}if (counter > REQUEST_LIMIT) {HttpServletResponse httpResponse = (HttpServletResponse) response;httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());try (PrintWriter writer = httpResponse.getWriter()) {writer.write("请求频率超过限制,请稍后再试!");}return;}chain.doFilter(request, response);}private String getClientIpAddress(HttpServletRequest request) {...return ipAddress;}
}

再优化一下加入布隆过滤器

使用布隆过滤器减少对Redis的访问:布隆过滤器是一种高效的概率数据结构,可以用于快速判断元素是否存在于集合中。在限制请求频率时,可以使用布隆过滤器来减少对Redis的访问。只有在布隆过滤器判断请求不是重复请求时,才进行Redis操作。

public class RequestLimitFilter implements Filter {@Autowiredprivate RedisTemplate<String, String> redisTemplate;private static final String REQUEST_LIMIT_PREFIX = "requestLimit:";private static final long REQUEST_LIMIT = 100; // 请求限制数量private static final long TIME_WINDOW = 60; // 时间窗口(单位:秒)private BloomFilter<String> bloomFilter;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化布隆过滤器int expectedInsertions = 1000; // 预期插入数量double falsePositiveProbability = 0.01; // 误判率bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), expectedInsertions, falsePositiveProbability);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;String ipAddress = getClientIpAddress(httpRequest);if (bloomFilter.mightContain(ipAddress)) {// 布隆过滤器判断可能是重复请求,直接放行chain.doFilter(request, response);return;}String key = REQUEST_LIMIT_PREFIX + ipAddress;Long counter;boolean isNewKey = false;try {counter = redisTemplate.opsForValue().increment(key, 1);if (counter == 1) {redisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS);isNewKey = true;}} catch (Exception e) {// 处理Redis操作异常// 可以选择记录日志或采取适当的处理措施e.printStackTrace();chain.doFilter(request, response);return;}if (counter > REQUEST_LIMIT) {if (isNewKey) {// 删除新创建的键,避免无限增长redisTemplate.delete(key);}HttpServletResponse httpResponse = (HttpServletResponse) response;httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());try (PrintWriter writer = httpResponse.getWriter()) {writer.write("请求频率超过限制,请稍后再试!");}return;}bloomFilter.put(ipAddress); // 将IP地址添加到布隆过滤器chain.doFilter(request, response);}@Overridepublic void destroy() {// 清理资源,如关闭Redis连接等}private String getClientIpAddress(HttpServletRequest request) {// 获取客户端IP地址的逻辑// ...}
}

在上述代码中,我们引入了布隆过滤器来减少对Redis的访问。如果布隆过滤器判断请求可能是重复请求,则直接放行,无需进行Redis操作。同时,我们还添加了对Redis操作异常的处理,并在限流超过阈值时删除新创建的键,以避免无限增长。请根据实际情况进行适当调整和完善。

  1. 注册过滤器
    在Spring Boot应用程序的配置类中注册过滤器,以便它能够在请求处理过程中生效。
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate RequestLimitFilter requestLimitFilter;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(requestLimitFilter);}
}

通过将过滤器添加到addInterceptors方法中,它将被注册为Spring Boot应用程序的全局过滤器,并在请求到达时执行限流逻辑。

4.总结

其实上面我们写完的还是有问题的

  1. 如果系统部署在多个节点上,可以考虑使用分布式限流算法,如令牌桶算法或漏桶算法。这些算法可以在分布式环境中平衡请求的处理,并保证全局的请求限制。
  2. 将请求限流的参数,如请求限制数量和时间窗口,配置为可动态调整的参数。可以使用注解或配置文件来管理这些参数,以便在运行时进行调整,而无需重新编译代码。

5.参考文档

  1. Spring Data Redis官方文档:https://docs.spring.io/spring-data/redis/docs/current/reference/html/ ↗
    这个文档提供了关于如何在Spring Boot中使用Spring Data Redis进行Redis操作的详细指南。 可以了解如何配置Redis连接、使用RedisTemplate进行操作以及其他高级功能。

6. Redis从入门到精通系列文章

  • 《Redis使用Lua脚本和Redisson来保证库存扣减中的原子性和一致性》
  • 《SpringBoot Redis 使用Lettuce和Jedis配置哨兵模式》
  • 《Redis【应用篇】之RedisTemplate基本操作》
  • 《Redis 从入门到精通【实践篇】之SpringBoot配置Redis多数据源》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis HyperLogLog 数据结构》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis地理位置数据结构GeoHash》
  • 《Redis 从入门到精通【进阶篇】之高可用哨兵机制(Redis Sentinel)详解》
  • 《Redis 从入门到精通【进阶篇】之redis主从复制详解》
  • 《Redis 从入门到精通【进阶篇】之Redis事务详解》
  • 《Redis从入门到精通【进阶篇】之对象机制详解》
  • 《Redis从入门到精通【进阶篇】之消息传递发布订阅模式详解》
  • 《Redis从入门到精通【进阶篇】之持久化 AOF详解》
  • 《Redis从入门到精通【进阶篇】之持久化RDB详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
  • 《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》
    在这里插入图片描述大家好,我是冰点,今天的Spring Boot+Redis 实现一个简单的限流器,全部内容就是这些。如果你有疑问或见解可以在评论区留言。

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

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

相关文章

每天一道leetcode:1129. 颜色交替的最短路径(图论中等广度优先遍历)

今日份题目&#xff1a; 给定一个整数 n&#xff0c;即有向图中的节点数&#xff0c;其中节点标记为 0 到 n - 1。图中的每条边为红色或者蓝色&#xff0c;并且可能存在自环或平行边。 给定两个数组 redEdges 和 blueEdges&#xff0c;其中&#xff1a; redEdges[i] [ai, bi…

用python来爬取某鱼的商品信息(2/2)

目录 上一篇文章 本章内容 设置浏览器为运行结束后不关闭&#xff08;可选&#xff09; 定位到搜索框的xpath地址 执行动作 获取cookie 保存为json文件 修改cookie的sameSite值并且导入cookie 导入cookie&#xff08;出错&#xff09; 导入cookie&#xff08;修改后&…

Tik Tok娱乐+电商MCN怎么做?

在美国外的热门市场中&#xff0c;TikTok 主要做的区域市场包括中东、拉美、欧洲和东亚&#xff0c;而这里面适合做电商的其实并不多。 欧洲、东亚都属于成熟市场&#xff0c;且 TikTok 本身在欧洲面临 DSA 法案更严格的审查&#xff0c;与在英国相同&#xff0c;欧洲各市场消…

【Vue】Vue2创建移动端项目实战教程,创建移动端项目保姆级教程,设置axios,utils工具包,vue.fonfig.js配置项 (下)

系列文章目录 这里是创建移动端项目 【Vue】Vue2.x创建项目全程讲解&#xff0c;保姆级教程&#xff0c;手把手教&#xff0c;Vue2怎么创建项目&#xff08;上&#xff09; 【Vue】Vue2创建移动端项目实战教程&#xff0c;创建移动端项目保姆级教程&#xff0c;接上一篇创建Vue…

ArcGIS入门操作手册

一.ArcGIS安装过程 参考本人博客&#xff1a;保姆级Arcgis安装图文安装教程_追忆苔上雪的博客-CSDN博客 二.ArcGIS植被指数计算 (1)使用工具&#xff1a;栅格计算器 打开软件&#xff0c;右侧搜索栅格计算器打开&#xff0c;要是搜索栏不小心叉掉找不到了&#xff0c;可以通…

Https、CA证书、数字签名

Https Http协议 Http协议是目前应用比较多应用层协议&#xff0c;浏览器对于Http协议已经实现。Http协议基本的构成部分有 请求行 &#xff1a; 请求报文的第一行请求头 &#xff1a; 从第二行开始为请求头内容的开始部分。每一个请求头都是由K-V键值对组成。请求体&#xf…

DoIP学习笔记系列:(五)“安全认证”的.dll从何而来?

文章目录 1. “安全认证”的.dll从何而来?1.1 .dll文件base1.2 增加客户需求算法传送门 DoIP学习笔记系列:导航篇 1. “安全认证”的.dll从何而来? 无论是用CANoe还是VFlash,亦或是编辑cdd文件,都需要加载一个与$27服务相关的.dll(Windows的动态库文件),这个文件是从哪…

【JavaWeb】MySQL约束、事务、多表查询

1 约束 PRIMARY KEY 主键约束 UNIQUE 唯一约束 NOT NULL 非空约束 DEFAULT 默认值约束 FOREIGN KEY 外键约束 主键 主键值必须唯一且非空&#xff1b;每个表必须有一个主键 建表时主键约束 CREATE TABLE 表名 (字段名 字段类型 PRIMARY KEY,字段名 字段类型 );CR…

Tomcat的多实例和动静分离

目录 一、多实例 二、 nginxtomcat的负载均衡和动静分离 三、Tomcat 客户端->四层代理->七层代理->tomcat服务器 实验&#xff1a; 问题总结&#xff1a; tomcat日志文件&#xff1a;/usr/local/tomcat/logs/catalina.out 一、多实例 在一台服务器上有多个tomc…

浅析前端请求登录与后台对接

首先确保前后端接口参数一致&#xff0c;我这里使用的是ant design Pro 前端框架 小技&#xff1a;shiftf6&#xff0c;全局重构&#xff0c;当接口不一致时很方便 前&#xff1a; 后&#xff1a; 前后端交互&#xff1a;前端需要向后端发送请求&#xff0c;前端ajax来请求后…

基于WebSocket的在线文字聊天室

与Ajax不同&#xff0c;WebSocket可以使服务端主动向客户发送响应&#xff0c;本案例就是基于WebSocket的一个在线聊天室&#xff0c;不过功能比较简单&#xff0c;只能满足文字交流。演示如下。 案例学习于b站up主&#xff0c;链接 。这位up主讲的非常清楚&#xff0c;值得去学…

Python脚本之连接MySQL【四】

本文为博主原创&#xff0c;未经授权&#xff0c;严禁转载及使用。 本文链接&#xff1a;https://blog.csdn.net/zyooooxie/article/details/124640412 之前写了篇 Python脚本之连接MySQL【三】&#xff0c;日常使用过程中&#xff0c;代码实际有很多改动&#xff0c;特此更新…

了解IL汇编循环

IL代码&#xff0c; .assembly extern mscorlib {}.assembly Test{.ver 1:0:1:0}.module test.exe.method static void main() cil managed{.maxstack 8.entrypoint.locals init (int32, int32)ldc.i4 4stloc.0 //Upper limit of the Loop, total 5 ldc.i4 0 stloc.…

5.文件共享

第四章 文件管理 5.文件共享 ​   假设此时系统中有两个用户User1和User2正在使用硬链接的方式来共享的使用文件1&#xff0c;而另一个用户User3想使用软连接的方式来共享这个文件1&#xff0c;那么User3会建立一个新的文件&#xff0c;这个文件是一个特殊的Link类型的文件&…

数据结构入门:栈

目录 前言 1. 栈 1.1栈的概念及结构 1.2 栈的实现 1.2.1 栈的定义 1.2.2 栈的初始化 1.2.3 入栈 1.2.4 出栈 1.2.5 栈的元素个数 1.2.6 栈顶数据 1.2.7 栈的判空 2.栈的应用 2.1 题目一&#xff1a;括号匹配 2.1.1 思路 2.1.2 分析 2.1.3 题解 总结 前言 无论你是计算机科学专…

算法笔试 java 输入输出练习

在线编程题刷题训练 所有答案 scancer函数的用法 输入输出总结top&#xff01;&#xff01;&#xff01;&#xff01; java如何调用函数&#xff08;方法&#xff09; java刷acm的各种输入输出 vscode配置java环境 子函数的调用&#xff0c;直接定义一个static子函数调用就…

c51单片机串口通信(中断方式接收数据)(单片机--单片机通信)示例代码 附proteus图

单片机一般采用中断方式接受数据&#xff0c;这样便于及时处理 #include "reg51.h" #include "myheader.h" #define uchar unsigned char int szc[10]{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; int bufferc[6]{0}; int sza[6]{0x01,0x02,0x0…

46.利用matlab绘制维安尼曲线(matlab程序)

1.代码 clear close all syms s t k u r; x12*sin(s)*cos(t);y12*sin(s)*sin(t);z12*cos(s); x2-2*cos(k)*cos(k);y22*sin(k)*cos(k);z2u; subplot(1,2,1);ezmeshc(x2,y2,z2,[0,pi,-2,2]); %绘制圆柱面 hold on; ezsurf(x1,y1,z1,[-pi,pi,0,pi]); %绘制球面 title( 球面与圆柱…

Windows11中使用OneDrive按Print Screen截屏按键,把截图自动保存到OneDrive中

参考&#xff1a;关于Onedrive 我已经勾选了自动保存屏幕截图 但是我截图之后我的图片并没有上传到onedrive上面 - Microsoft Community 1. 打开Windows 11的设置&#xff0c;可以通过按下Win I键来快速打开设置&#xff1b; 2. 设置--辅助功能--键盘--使用”print Screen“键…

ChatGPT能代替搜索引擎吗?ChatGPT和搜索引擎有什么区别?

ChatGPT和搜索引擎是两种在信息获取和交流中常用的工具&#xff0c;ChatGPT是一种基于人工智能技术的聊天机器人&#xff0c;而搜索引擎是一种在互联网上搜索信息的工具。尽管它们都是依托互联网与信息获取和交流有关&#xff0c;部分功能重合&#xff0c;但在很多方面存在着明…