8. 基于 Redis 实现限流
在高并发的分布式系统中,限流是保证服务稳定性的重要手段之一。通过限流机制,可以控制系统处理请求的频率,避免因瞬时流量过大导致系统崩溃。Redis 是一种高效的缓存数据库,具备丰富的数据结构和原子操作,适合用来实现分布式环境下的限流。
本文将结合 Spring Boot 和 Redis,详细讲解如何实现基于 Redis 的限流功能,包括应用场景、实现原理、具体过程以及效果分析。
一、限流的应用场景
限流在各种场景中扮演着重要角色,以下是几个典型的使用场景:
- 接口防刷:防止恶意用户或爬虫频繁访问某些接口,导致服务负载过高。
- API 调用频率控制:对外部 API 提供服务时,限制用户调用频率,避免超出系统处理能力。
- 短信验证码:发送短信验证码时限制同一用户的发送频率,防止滥用。
- 交易场景:在抢购或秒杀系统中,限制用户请求的频次,防止瞬时高并发请求导致服务器宕机。
二、限流的实现原理
Redis 实现限流通常采用以下几种方式:
- 固定窗口限流:在固定的时间窗口内,限制请求的次数。例如每分钟最多允许 100 次请求。
- 滑动窗口限流:将固定时间窗口划分为多个小的时间段,保证限流更平滑和公平。
- 令牌桶算法:限制访问速率,按照固定的速率往令牌桶中放置令牌,用户每次请求必须获取一个令牌才能通过。
- 漏桶算法:按照固定的速率处理请求,若请求过多则溢出丢弃。
本文主要使用固定窗口和滑动窗口两种方法进行 Redis 限流的实现。
三、基于 Redis 实现限流的步骤
1. 项目配置
首先,创建一个 Spring Boot 项目,并引入 Redis 相关依赖。
<dependencies><!-- Spring Boot Starter for Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Spring Boot Web Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
</dependencies>
在 application.properties
中配置 Redis 连接:
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=yourpassword
2. 实现固定窗口限流
固定窗口限流的核心思路是,每个用户在一个固定时间窗口(如 1 分钟)内只能发起 N 次请求。Redis 提供的 INCR
和 EXPIRE
命令可以高效地实现这一限流机制。
我们可以通过以下步骤来实现固定窗口限流:
- 检查 Redis 中该用户的访问计数,如果不存在则创建,并设置有效期为 1 分钟。
- 如果计数未达到阈值,允许访问并增加计数。
- 如果计数超过阈值,拒绝请求。
首先,创建 RateLimiterUtil
工具类。
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Component
public class RateLimiterUtil {private final StringRedisTemplate redisTemplate;public RateLimiterUtil(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}/*** 基于固定窗口的限流* @param key 限流标识* @param limit 限流次数* @param windowSize 时间窗口大小(秒)* @return 是否允许访问*/public boolean isAllowed(String key, int limit, long windowSize) {Long count = redisTemplate.opsForValue().increment(key);if (count == 1) {// 第一次访问,设置过期时间redisTemplate.expire(key, windowSize, TimeUnit.SECONDS);}return count <= limit;}
}
3. 使用固定窗口限流进行接口请求控制
接下来,我们可以在需要限流的接口上使用 RateLimiterUtil
来控制请求的频率。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ApiController {@Autowiredprivate RateLimiterUtil rateLimiterUtil;private static final String LIMIT_KEY = "api_limit";@GetMapping("/api/resource")public String getResource() {// 限制每个用户每分钟最多访问 10 次boolean isAllowed = rateLimiterUtil.isAllowed(LIMIT_KEY, 10, 60);if (!isAllowed) {return "访问频率过高,请稍后再试";}return "访问成功";}
}
4. 实现滑动窗口限流
相比固定窗口,滑动窗口限流可以更细粒度地控制请求频率,避免流量高峰时集中在某个时间段。
滑动窗口的核心思想是,将一个大时间窗口划分为多个小的时间段,每次请求都会在 Redis 中记录当前时间段的请求次数,并删除过期的时间段数据。
可以通过 Redis 的 ZADD
(有序集合)来记录请求的时间戳,结合 ZRANGE
和 ZREM
来计算当前窗口内的请求次数。
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.time.Instant;@Component
public class SlidingWindowRateLimiter {private final StringRedisTemplate redisTemplate;public SlidingWindowRateLimiter(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}/*** 基于滑动窗口的限流* @param key 限流标识* @param limit 限流次数* @param windowSize 时间窗口大小(秒)* @return 是否允许访问*/public boolean isAllowed(String key, int limit, long windowSize) {long now = Instant.now().getEpochSecond();long windowStart = now - windowSize;// 使用 Redis 的有序集合记录请求时间redisTemplate.opsForZSet().add(key, String.valueOf(now), now);// 移除窗口之外的数据redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);// 统计窗口内的请求数Long requestCount = redisTemplate.opsForZSet().zCard(key);if (requestCount != null && requestCount > limit) {return false;}return true;}
}
5. 使用滑动窗口限流控制接口访问
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class SlidingApiController {@Autowiredprivate SlidingWindowRateLimiter slidingWindowRateLimiter;private static final String LIMIT_KEY = "sliding_api_limit";@GetMapping("/api/sliding_resource")public String getResource() {// 限制每个用户每分钟最多访问 10 次boolean isAllowed = slidingWindowRateLimiter.isAllowed(LIMIT_KEY, 10, 60);if (!isAllowed) {return "访问频率过高,请稍后再试";}return "访问成功";}
}
四、限流效果分析
- 性能与效率:基于 Redis 的限流具有较高的性能,
INCR
、EXPIRE
和ZADD
等操作都具备原子性,且 Redis 本身可以高效处理大量并发请求,适用于分布式系统。 - 限流精度:固定窗口限流简单易实现,适用于对请求频率没有精细控制要求的场景;滑动窗口限流能够提供更加平滑的限流体验,避免了流量高峰。
- 分布式扩展:Redis 支持分布式部署,适用于多实例环境中的限流需求,能够保证多个节点对同一用户的请求进行统一控制。
五、其他优化与改进建议
- 限流规则动态调整:可以通过 Redis 订阅发布模式(Pub/Sub)实现限流规则的动态调整,适应不同的流量需求。
- 用户维度限流:在实际应用中,限流往往根据不同用户、IP 地址等维度进行,可以通过
key
动态拼接用户 ID 或 IP 来实现多维度限流。 - 分布式缓存与 Redis 哨兵:使用 Redis 的哨兵机制或集群模式来提高限流系统的可用性。
相关文章:

8. 基于 Redis 实现限流
在高并发的分布式系统中,限流是保证服务稳定性的重要手段之一。通过限流机制,可以控制系统处理请求的频率,避免因瞬时流量过大导致系统崩溃。Redis 是一种高效的缓存数据库,具备丰富的数据结构和原子操作,适合用来实现…...

241117学习日志——[CSDIY] [ByteDance] 后端训练营 [05]
CSDIY:这是一个非科班学生的努力之路,从今天开始这个系列会长期更新,(最好做到日更),我会慢慢把自己目前对CS的努力逐一上传,帮助那些和我一样有着梦想的玩家取得胜利!!&…...

蓝桥杯备赛(持续更新)
16届蓝桥杯算法类知识图谱.pdf 1. 格式打印 %03d:如果是两位数,将会在前面添上一位0 %.2f:会保留两位小数 如果是long,必须在数字后面加上L。 2. 进制转化 2.1. 十进制转任意进制: 十进制转任意进制时ÿ…...

k8s 学习笔记之 k8s 存储管理
文章目录 概述卷卷的常用类型emptyDir边车容器 HostPathnfsPV/PVC静态供给 PV 和 PVC创建静态 PV创建 pvc创建 pod 应用 pvc 动态供给 PV 和 PVC创建 StorageClass创建 pvc创建 pod 使用 pvc PV 的生命周期 内置存储对象ConfigMapSecret 配置文件自动重新加载方案**1. 应用内动…...

ios swift开发--ios远程推送通知配置
远程推送通知(Push Notifications)在 iOS 平台上是免费提供的,但需要一些准备工作。以下是开通和使用远程推送通知的基本步骤: 开通远程推送通知 注册 Apple Developer Program: 访问 Apple Developer 并注册一个开发…...

【JavaEE进阶】CSS
本节⽬标 掌握 CSS 基本语法规范和CSS选择器的各种⽤法, 熟练使⽤CSS的常⽤属性. 一.CSS介绍 1.什么是CSS? CSS(Cascading Style Sheet),层叠样式表, ⽤于控制⻚⾯的样式. CSS 能够对⽹⻚中元素位置的排版进⾏像素级精确控制, 实现美化⻚⾯的效果. 能够做到⻚⾯…...

基于Java Springboot宠物领养救助平台
一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数据…...

C/C++ 中有哪些类型转换方式? 分别有什么区别?
在C编写C/C代码的时候,我们经常会遇到发生类型转换的场景,比如 赋值运算符的两个操作数不同、实参和形参类型不同、函数返回值类型和接收返回值的类型不同,都会发生类型转换;所以,在C语言中提供了两种类型转换 —— 隐…...

小程序租赁系统开发为企业提供高效便捷的租赁服务解决方案
内容概要 在这个数字化飞速发展的时代,小程序租赁系统应运而生,成为企业管理租赁业务的一种新选择。随着移动互联网的普及,越来越多的企业开始关注如何利用小程序来提高租赁服务的效率和便捷性。小程序不仅可以为用户提供一个快速、易用的平…...

Scala的Array
数组:物理空间上连续的(一个挨一个) 优势:根据下标,能快速找到元素 列表:物理空间上不连续(不是一个元素挨着一个元素) 优势:插入元素,删除比较快 object…...

等保测评怎么做?具体流程是什么?
等保测评是对信息系统进行等保(等级保护)安全评测的过程。等保是指对信息系统进行等级化保护管理,目的是提高信息系统的安全性,防止信息泄露、篡改、破坏等安全问题。哈尔滨等保测评按照《中华人民共和国网络安全法》及《信息安全…...

基于YOLOv8深度学习的汽车车身车损检测系统研究与实现(PyQt5界面+数据集+训练代码)
本文研究并实现了一种基于YOLOV8深度学习模型的汽车车身车损检测系统,旨在解决传统车损检测中效率低、精度不高的问题。该系统利用YOLOV8的目标检测能力,在单张图像上实现了车身损坏区域的精确识别和分类,尤其是在车身凹痕、车身裂纹和车身划…...

力扣 LeetCode 144. 二叉树的前序遍历(Day6:二叉树)
解题思路: 方法一:递归(中左右) class Solution {List<Integer> res new ArrayList<>();public List<Integer> preorderTraversal(TreeNode root) {recur(root);return res;}public void recur(TreeNode roo…...

Adobe Illustrator(Ai)修图软件入门操作参考,收集查过的各个细节用法
到现在,对于Ai的使用也是一半一半,基本上都是用到啥就查啥。因为用得也不是很频繁,脑子也记不住很多操作,所以有时候靠肌肉记忆,很多时候,得再百度一遍…… 所以 我在这再备份一下,做个搬运工 …...

Apache Paimon、Apache Hudi、Apache Iceberg对比分析
Apache Paimon、Apache Hudi、Apache Iceberg 都是面向大数据湖的表格式存储管理框架。它们各自的架构、数据管理方式以及适用场景有所不同。下面是对三者的详细对比分析: 1. 基本简介 Apache Paimon: Paimon 是一个新兴的数据湖存储引擎,旨在支持流批一体的数据处理和管理…...

[ 网络安全介绍 5 ] 为什么要学习网络安全?
🍬 博主介绍 👨🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…...

生产环境centos8 Red Hat8部署ansible and 一键部署mysql两主两从ansible脚本预告
一、各节点服务器创建lvm逻辑卷组 1.初始化磁盘为物理卷(PV) 命令:sudo pvcreate /dev/vdb 2.创建卷组(VG) 命令:sudo vgcreate db_vg /dev/vdb 3.创建逻辑卷(LV) 命令:s…...

华为云stack网络服务流量走向
1.同VPC同子网同主机内ECS间互访流量走向 一句话通过主机内部br-int通信 2.同VPC同子网跨主机ECS间互访流量走向 3.同VPC不同子网同主机ECS间互访流量走向 查看ECS配置文件底层KVM技术 查看日志 查看ECS的ID号(管理员身份查询所有租户信息) 查看ECS的其…...

嵌入式硬件杂谈(二)-芯片输入接入0.1uf电容的本质(退耦电容)
引言:对于嵌入式硬件这个庞大的知识体系而言,太多离散的知识点很容易疏漏,因此对于这些容易忘记甚至不明白的知识点做成一个梳理,供大家参考以及学习,本文主要针对芯片输入接入0.1uf电容的本质的知识点的进行学习。 目…...

计算机网络HTTP——针对实习面试
目录 计算机网络HTTP什么是HTTP?HTTP和HTTPS有什么区别?分别说明HTTP/1.0、HTTP/2.0、HTTP/3.0请说明访问网页的全过程请说明HTTP常见的状态码Cookie和Session有什么区别?HTTP请求方式有哪些?请解释GET和POST的区别?HT…...

JAVA中对象实体与对象引用有何不同?举例说明
在 Java 中,对象实体(Object instance)和对象引用(Object reference)是两个不同的概念,虽然它们通常被一起讨论,但它们的作用和表现方式是不同的。下面我们来详细说明这两者的区别。 1. 对象实体…...

C++设计思想-001-设计模式-单例模式
1.单例模式优点 保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享; 实现: 1.1 单例模式的类只提供私有的构造函数 1.2类定义中含有一个该类的静态私有对象 1.3该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象 2.单…...

远程连接服务器
1、远程连接服务器简介 ssh secure shell 非对称加密:一对公钥私钥 对称加密:加密和解密使用的是同一把密钥;(同一秘钥既可以进行加密也可以进行解密 )优势:使用一个秘钥它的加密效率高一些(快一些) …...

【分布式技术】ES扩展知识-Elasticsearch分词器的知识与选择
ES知识扩展 分词器有哪些?1. 标准分词器(Standard Analyzer):示例示例文本分析配置参数与自定义应用场景 2. Simple Analyzer:示例示例文本分析应用场景与限制结论 3. Whitespace Analyzer:示例示例文本分析…...

【网络安全 | 漏洞挖掘】通过密码重置污染实现账户接管
未经许可,不得转载。 文章目录 密码重置污染攻击漏洞挖掘的过程目标选择与初步测试绕过 Cloudflare 的尝试发现两个域名利用 Origin 头部污染实现账户接管攻击流程总结在今天的文章中,我们将深入探讨一种 账户接管 漏洞,并详细分析如何绕过 Cloudflare 的保护机制,利用密码…...

【Nginx从入门到精通】01 、教程简介
讲师:张一鸣老师 课程简介 重量级课程 由浅入深,内容非常广泛 几十个线上的实战案例(图谱),几乎涵盖当前所有互联网主流应用场景 性能:由压测得出结果 调优:从操作系统开始,使你对高并发系统架构的技…...

MySQL面试之底层架构与库表设计
华子目录 mysql的底层架构客户端连接服务端连接的本质,连接用完会立马丢弃吗解析器和优化器的作用sql执行前会发生什么客户端的连接池和服务端的连接池数据库的三范式 mysql的底层架构 客户端连接服务端 连接的本质,连接用完会立马丢弃吗 解析器和优化器…...

C2 追踪器:监控指挥与控制的重要性
12 款暗网监控工具 20 款免费网络安全工具 移动取证软件:为什么 Belkasoft X 应该是您的首选工具 网络安全已成为不断演变的威胁形势中的关键领域。 网络攻击者经常使用命令和控制 (C2) 基础设施来执行和管理攻击。 这些基础设施使恶意软件和攻击者能够与受害设…...

二、神经网络基础与搭建
神经网络基础 前言一、神经网络1.1 基本概念1.2 工作原理 二、激活函数2.1 sigmoid激活函数2.1.1 公式2.1.2 注意事项 2.2 tanh激活函数2.2.1 公式2.2.2 注意事项 2.3 ReLU激活函数2.3.1 公式2.3.2 注意事项 2.4 SoftMax激活函数2.4.1 公式2.4.2 Softmax的性质2.4.3 Softmax的应…...

java导出pdf
引入包 <properties><itext.version>8.0.5</itext.version></properties><dependencies><dependency><groupId>com.itextpdf</groupId><artifactId>itext-core</artifactId><version>${itext.version}</…...