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

滑动窗口限流算法:基于Redis有序集合的实现与优化

滑动窗口限流算法是一种基于时间窗口的流量控制策略,它将时间划分为固定大小的窗口,并在每个窗口内记录请求次数。通过动态滑动窗口,算法能够灵活调整限流速率,以应对流量的波动。

算法核心步骤

  1. 统计窗口内的请求数量:记录当前时间窗口内的请求次数。
  2. 应用限流规则:根据预设的阈值判断是否允许当前请求通过。

Redis有序集合的应用

Redis的有序集合(Sorted Set)为滑动窗口限流提供了理想的实现方式。每个有序集合的成员都有一个分数(score),我们可以利用分数来定义时间窗口。每当有请求进入时,将当前时间戳作为分数,并将请求的唯一标识作为成员添加到集合中。这样,通过统计窗口内的成员数量,即可实现限流。

实现细节

Redis命令简化

通过Redis的ZADD命令,我们可以将请求的时间戳和唯一标识添加到有序集合中:

ZADD 资源标识 时间戳 请求标识
Java代码实现

以下是基于Java和Redis的滑动窗口限流实现:

public boolean isAllow(String key) {ZSetOperations<String, String> zSetOperations = stringRedisTemplate.opsForZSet();long currentTime = System.currentTimeMillis();long windowStart = currentTime - period;zSetOperations.removeRangeByScore(key, 0, windowStart);Long count = zSetOperations.zCard(key);if (count >= threshold) {return false;}String value = "请求唯一标识(如:请求流水号、哈希值、MD5值等)";zSetOperations.add(key, value, currentTime);stringRedisTemplate.expire(key, period, TimeUnit.MILLISECONDS);return true;
}
Lua脚本优化

为了确保在高并发场景下的原子性操作,我们可以将上述逻辑封装为Lua脚本:

local key = KEYS[1]
local current_time = tonumber(ARGV[1])
local window_size = tonumber(ARGV[2])
local threshold = tonumber(ARGV[3])
redis.call('ZREMRANGEBYSCORE', key, 0, current_time - window_size)
local count = redis.call('ZCARD', key)
if count >= threshold thenreturn tostring(0)
elseredis.call('ZADD', key, tostring(current_time), current_time)return tostring(1)
end
完整Java代码
package com.example.demo.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;import java.util.Collections;
import java.util.concurrent.TimeUnit;@Service
public class SlidingWindowRatelimiter {private long period = 60 * 1000; // 1分钟private int threshold = 3; // 3次@Autowiredprivate StringRedisTemplate stringRedisTemplate;public boolean isAllow(String key) {ZSetOperations<String, String> zSetOperations = stringRedisTemplate.opsForZSet();long currentTime = System.currentTimeMillis();long windowStart = currentTime - period;zSetOperations.removeRangeByScore(key, 0, windowStart);Long count = zSetOperations.zCard(key);if (count >= threshold) {return false;}String value = "请求唯一标识(如:请求流水号、哈希值、MD5值等)";zSetOperations.add(key, value, currentTime);stringRedisTemplate.expire(key, period, TimeUnit.MILLISECONDS);return true;}public boolean isAllow2(String key) {String luaScript = "local key = KEYS[1]\n" +"local current_time = tonumber(ARGV[1])\n" +"local window_size = tonumber(ARGV[2])\n" +"local threshold = tonumber(ARGV[3])\n" +"redis.call('ZREMRANGEBYSCORE', key, 0, current_time - window_size)\n" +"local count = redis.call('ZCARD', key)\n" +"if count >= threshold then\n" +" return tostring(0)\n" +"else\n" +" redis.call('ZADD', key, tostring(current_time), current_time)\n" +" return tostring(1)\n" +"end";long currentTime = System.currentTimeMillis();DefaultRedisScript<String> redisScript = new DefaultRedisScript<>(luaScript, String.class);String result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), String.valueOf(currentTime), String.valueOf(period), String.valueOf(threshold));return "1".equals(result);}
}

AOP实现限流

为了更方便地应用限流策略,我们可以通过AOP(面向切面编程)来拦截请求并应用限流规则。

自定义注解

首先,定义一个限流注解:

package com.example.demo.controller;import java.lang.annotation.*;@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {long period() default 60; // 窗口大小(默认:60秒)long threshold() default 3; // 阈值(默认:3次)
}
切面实现

然后,实现一个切面来拦截带有@RateLimit注解的方法:

package com.example.demo.controller;import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Slf4j
@Aspect
@Component
public class RateLimitAspect {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Before("@annotation(rateLimit)")public void doBefore(JoinPoint joinPoint, RateLimit rateLimit) {long period = rateLimit.period();long threshold = rateLimit.threshold();HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String uri = httpServletRequest.getRequestURI();Long userId = 123L; // 模拟获取用户IDString key = "limit:" + userId + ":" + uri;ZSetOperations<String, String> zSetOperations = stringRedisTemplate.opsForZSet();long currentTime = System.currentTimeMillis();long windowStart = currentTime - period * 1000;zSetOperations.removeRangeByScore(key, 0, windowStart);Long count = zSetOperations.zCard(key);if (count >= threshold) {throw new RuntimeException("请求过于频繁!");} else {zSetOperations.add(key, String.valueOf(currentTime), currentTime);stringRedisTemplate.expire(key, period, TimeUnit.SECONDS);}}
}
使用注解

最后,在需要限流的方法上添加@RateLimit注解:

@RestController
@RequestMapping("/hello")
public class HelloController {@RateLimit(period = 30, threshold = 2)@GetMapping("/sayHi")public void sayHi() {}
}

总结

通过Redis有序集合和Lua脚本,我们实现了一个高效且灵活的滑动窗口限流算法。结合AOP,我们可以轻松地将限流策略应用到具体的业务方法中。对于更复杂的流量控制需求,可以参考阿里巴巴的Sentinel框架。

参考链接:

  • Sentinel官方文档
  • AOP实现限流
  • Redis Lua脚本

相关文章:

滑动窗口限流算法:基于Redis有序集合的实现与优化

滑动窗口限流算法是一种基于时间窗口的流量控制策略&#xff0c;它将时间划分为固定大小的窗口&#xff0c;并在每个窗口内记录请求次数。通过动态滑动窗口&#xff0c;算法能够灵活调整限流速率&#xff0c;以应对流量的波动。 算法核心步骤 统计窗口内的请求数量&#xff1…...

Angular 最新版本和 Vue 对比完整指南

1. Angular 最新版本 当前 Angular 最新稳定版本是 Angular 17(2024年初) 2. 主要区别对比表 特性 | Angular | Vue 框架类型 | 完整框架 | 渐进式框架 默认语言 | TypeScript | JavaScript/TypeScript 数据处理 | RxJS | Promise/async/await 架构特点 | 依赖注入,…...

DAY39|动态规划Part07|LeetCode:198.打家劫舍、213.打家劫舍II、337.打家劫舍III

目录 LeetCode:198.打家劫舍 基本思路 C代码 LeetCode:213.打家劫舍II 基本思路 C代码 LeetCode:337.打家劫舍III 基本思路 C代码 LeetCode:198.打家劫舍 力扣题目链接 文字讲解&#xff1a;LeetCode:198.打家劫舍 视频讲解&#xff1a;动态规划&#xff0c;偷不偷这个…...

MYSQL----------------sql 优化

优化 SQL 语句的一般步骤 1. 了解 SQL 的执行频率 SHOW STATUS LIKE Com_%;代码解释&#xff1a; SHOW STATUS LIKE Com_%;&#xff1a;此命令可以查看各种 SQL 语句的执行频率&#xff0c;例如 Com_select 表示 SELECT 语句的执行次数&#xff0c;Com_insert 表示 INSERT 语…...

深度学习中的正则化方法

最近看到了正则化的内容&#xff0c;发现自己对正则化的理解已经忘得差不多了&#xff0c;这里在整理一下&#xff0c;方便以后查阅。 深度学习中的正则化方法 1. L2 正则化&#xff08;L2 Regularization&#xff09;2. L1 正则化&#xff08;L1 Regularization&#xff09;3.…...

前端报告 2024:全新数据,深度解析未来趋势

温馨提示: 此报告为国际版全球报告,其中所涉及的技术应用、工具偏好、开发者习惯等情况反映的是全球前端开发领域的综合态势。由于国内外技术发展环境、行业生态以及企业需求等存在差异,可能有些内容并不完全契合国内的实际情况,请大家理性阅读,批判性地吸收其中的观点与信…...

计算机网络之---子网划分与IP地址

子网划分与IP地址的关系 在计算机网络中&#xff0c;子网划分&#xff08;Subnetworking&#xff09;是将一个网络划分为多个子网络的过程。通过子网划分&#xff0c;可以有效地管理和利用IP地址空间&#xff0c;提高网络的性能、安全性和管理效率。 子网划分的基本目的是通过…...

计算机网络 (31)运输层协议概念

一、概述 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。运输层的一个核心功能是提供从源端主机到目的端主机的可靠的、与实际使用的网络无关的信息传输。它向高层用…...

代码随想录算法训练营day28

代码随想录算法训练营 —day28 文章目录 代码随想录算法训练营前言一、122.买卖股票的最佳时机II二、55. 跳跃游戏三、跳跃游戏 II方法一方法二 1005. K 次取反后最大化的数组和总结 前言 今天是算法营的第28天&#xff0c;希望自己能够坚持下来&#xff01; 今日任务&#x…...

建立时间和保持时间

建立时间 在时钟有效沿到来之前&#xff0c;数据必须维持一段时间保持不变&#xff0c;这段时间就是建立时间 Tsetup 1 基本概念 建立时间&#xff08;Setup Time&#xff09;&#xff1a; 在 SystemVerilog 中&#xff0c;建立时间是指在时钟信号的有效边沿&#xff08;例如…...

vue,router路由传值问题,引用官方推荐

参考贴https://blog.csdn.net/m0_57033755/article/details/129927829 根据官方文档的更新日志&#xff0c;建议使用state传值 官方文档更新日志 实际的console结果 传值 router.push({ name: KnowledgeDetail, state: { params } });接收值 const historyParams histor…...

AIDD-人工智能药物设计-AlphaFold系列:年终回顾,AlphaFold迄今为止的实际应用案例

AlphaFold系列&#xff1a;年终回顾&#xff0c;AlphaFold迄今为止的实际应用案例 01 引言 AlphaFold由 DeepMind 团队开发&#xff0c;最初在蛋白质结构预测竞赛 CASP 中惊艳亮相。随着 AlphaFold2 和后续版本的迭代进步&#xff0c;其精度和通用性不断提升&#xff0c;逐渐走…...

Scala语言的面向对象编程

Scala语言的面向对象编程 引言 在当今的软件开发中&#xff0c;面向对象编程&#xff08;OOP&#xff09;是一种非常强大且广泛使用的编程范式。Scala是一种现代编程语言&#xff0c;结合了面向对象编程和函数式编程的特性&#xff0c;非常适合用于大规模软件的开发。本文将介…...

MySQL学习记录1【DQL和DCL】

SQL学习记录 该笔记从DQL处开始记录 DQL之前值得注意的点 字段 BETWEEN min AND max 可以查询区间[min, max]的数值如果同一个字段需要满足多个OR条件&#xff0c;可以采取 字段 IN(数值1, 数值2, 数值3....)LIKE语句 字段 LIKE ___%%% 表示模糊匹配&#xff0c;_匹配一个字段…...

验证码转发漏洞

开发人员有时候会以数组的形式接收用户的手机号并遍历执行&#xff0c;这时就可以在注册或登录页面填写两个手机号并点击发送验证码&#xff0c;这两个手机号会同时收到相同验证码&#xff0c;可以用任意一个手机号登录或注册&#xff0c;即验证码转发漏洞。 1、burpsuite内置…...

使用 C++ 实现神经网络:从基础到高级优化

引言 在现代机器学习中&#xff0c;神经网络已经成为最重要的工具之一。虽然 Python 提供了诸如 TensorFlow、PyTorch 等强大的机器学习库&#xff0c;但如果你想深入理解神经网络的实现原理&#xff0c;或者出于某些性能、资源限制的考虑&#xff0c;使用 C 来实现神经网络会是…...

【WRF运行报错】总结WRF运行时报错及解决方案(持续更新)

目录 ./real.exe错误1:ERROR while reading namelist physics./wrf.exe错误1:FATAL CALLED FROM FILE: <stdin> LINE: 2419 Warning: too many input landuse types参考./real.exe 错误1:ERROR while reading namelist physics 执行./real.exe时,报错如下: taski…...

Kotlin语言的循环实现

Kotlin语言中的循环实现 Kotlin是一种现代的、跨平台的编程语言&#xff0c;广泛应用于Android开发、后端服务及多种其他软件开发领域。与Java类似&#xff0c;Kotlin也支持多种循环结构&#xff0c;包括for循环、while循环和do while循环。掌握这些循环结构是每个Kotlin开发者…...

基于CNN的人脸识别考勤管理系统实现

随着技术的不断进步&#xff0c;人脸识别技术已经在各行各业得到了广泛的应用&#xff0c;尤其在 考勤管理 上&#xff0c;它提供了更加智能、便捷、精准的解决方案。本篇博客将介绍如何基于 PyQt5 和 MySQL 实现一个 人脸识别考勤系统&#xff0c;并通过具体代码展示如何通过图…...

Android基于回调的事件处理

Android 中的回调机制&#xff1a;基于回调的事件处理详解 在 Android 开发中&#xff0c;回调&#xff08;Callback&#xff09;是一种常见的事件处理机制&#xff0c;主要用于异步操作和事件通知。与传统的基于监听器的事件处理相比&#xff0c;回调机制更加灵活、通用&…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

C++八股 —— 单例模式

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

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...