Redis+注解实现限流机制(IP、自定义等)
简介
在项目的使用过程中,限流的场景是很多的,尤其是要提供接口给外部使用的时候,但是自己去封装的话,相对比较耗时。
本方式可以使用默认(方法),ip、自定义参数进行限流,根据时间和次数进行。
整合步骤
依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.27</version></dependency><dependency><groupId>com.googlecode.aviator</groupId><artifactId>aviator</artifactId><version>5.4.1</version><scope>compile</scope></dependency>
限流注解
package com.walker.ratelimiter.annotation;import com.walker.ratelimiter.enums.LimitType;import java.lang.annotation.*;@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimiter {/*** 限流key*/String key() default "rate_limit";/*** 限流时间,单位秒*/int time() default 60;/*** 限流次数*/int count() default 50;/*** 限流类型*/LimitType limitType() default LimitType.DEFAULT;/*** 自定义编码* 支持SPEL表达式* 如果使用多参数,则使用:分割**/String customerCode() default "";/*** 自定义编码分割符*/String customerCodeSplit() default ":";
}
限流配置:获取限流lua脚本
package com.walker.ratelimiter.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;@Configuration
public class RateLimitConfig {@Beanpublic DefaultRedisScript<Long> limitScript() {DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/limit.lua")));redisScript.setResultType(Long.class);return redisScript;}}
基础变量
package com.walker.ratelimiter.constants;public interface BaseConstants {String COLON = ":";
}
枚举类型
package com.walker.ratelimiter.enums;public enum LimitType {/*** 默认策略*/DEFAULT,/*** 根据IP进行限流*/IP,/*** 自定义*/CUSTOME,}
lua脚本

local key = KEYS[1]
local count = tonumber(ARGV[1])
local time = tonumber(ARGV[2])
local current = redis.call('get', key)
if current and tonumber(current) > count thenreturn tonumber(current)
end
current = redis.call('incr', key)
if tonumber(current) == 1 thenredis.call('expire', key, time)
end
return tonumber(current)
切面类
package com.walker.ratelimiter.aspect;import cn.hutool.core.util.StrUtil;
import com.walker.ratelimiter.annotation.RateLimiter;
import com.walker.ratelimiter.constants.BaseConstants;
import com.walker.ratelimiter.enums.LimitType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.List;@Slf4j
@Aspect
@Component
public class RateLimiterAspect {private final RedisTemplate redisTemplate;private final RedisScript<Long> limitScript;private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();public RateLimiterAspect(RedisTemplate redisTemplate, RedisScript<Long> limitScript) {this.redisTemplate = redisTemplate;this.limitScript = limitScript;}@Around("@annotation(com.walker.ratelimiter.annotation.RateLimiter)")public Object doBefore(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();RateLimiter rateLimiter = methodSignature.getMethod().getAnnotation(RateLimiter.class);//判断该方法是否存在限流的注解if (null != rateLimiter) {//获得注解中的配置信息int count = rateLimiter.count();int time = rateLimiter.time();//调用getCombineKey()获得存入redis中的key key -> 注解中配置的key前缀-ip地址-方法路径-方法名String combineKey = getCombineKey(rateLimiter, methodSignature, joinPoint);log.info("combineKey->,{}", combineKey);//将combineKey放入集合List<Object> keys = Collections.singletonList(combineKey);log.info("keys->", keys);try {//执行lua脚本获得返回值Long number = (Long) redisTemplate.execute(limitScript, keys, count, time);//如果返回null或者返回次数大于配置次数,则限制访问if (number == null || number.intValue() > count) {throw new RuntimeException("访问过于频繁,请稍候再试");}log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new RuntimeException("服务器限流异常,请稍候再试");}}return joinPoint.proceed();}/*** Gets combine key.** @param rateLimiter the rate limiter* @param signature the signature* @param joinPoint* @return the combine key*/public String getCombineKey(RateLimiter rateLimiter, MethodSignature signature, ProceedingJoinPoint joinPoint) throws UnknownHostException {StringBuilder stringBuffer = new StringBuilder(rateLimiter.key());
// ip限流if (rateLimiter.limitType() == LimitType.IP) {InetAddress ip = InetAddress.getLocalHost();log.info("获取ip地址为:{}", ip);String hostAddress = ip.getHostAddress();stringBuffer.append(hostAddress).append(BaseConstants.COLON);
// 自定义编码限流} else if (rateLimiter.limitType() == LimitType.CUSTOME) {if (StrUtil.isEmpty(rateLimiter.customerCode())) {throw new RuntimeException("自定义编码不能为空");}String customerCode = rateLimiter.customerCode();String split = rateLimiter.customerCodeSplit();String[] customerCodes = customerCode.split(split);for (String code : customerCodes) {ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();EvaluationContext evaluationContext = new MethodBasedEvaluationContext(TypedValue.NULL, signature.getMethod(), joinPoint.getArgs(), parameterNameDiscoverer);Expression expression = spelExpressionParser.parseExpression(code);String resolvedCustomerCode = String.valueOf(expression.getValue(evaluationContext));if(StrUtil.isEmpty(resolvedCustomerCode)){throw new RuntimeException("自定义编码不能为空");}stringBuffer.append(BaseConstants.COLON).append(resolvedCustomerCode);}}Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();stringBuffer.append(BaseConstants.COLON).append(targetClass.getName()).append(BaseConstants.COLON).append(method.getName());return stringBuffer.toString();}}
使用
- 根据ip进行限流
limitType = LimitType.IP
- 默认
limitType = LimitType.DEFAULT
- 自定义参数限流
使用Spel表达式,从参数中获取自定义的code,然后60s限流5次
@RateLimiter(limitType = LimitType.CUSTOME,customerCode = "#form.appCode:#toUserInfo.userUid",count = 5,time = 60)
public Result<Boolean> message(ImSendMsgForm form, MissuUsers toUserInfo) {}
相关文章:
Redis+注解实现限流机制(IP、自定义等)
简介 在项目的使用过程中,限流的场景是很多的,尤其是要提供接口给外部使用的时候,但是自己去封装的话,相对比较耗时。 本方式可以使用默认(方法),ip、自定义参数进行限流,根据时间…...
SAP从入门到放弃系列之委外分包(Subcontracting)-Part1
以前写过一篇委外相关的文章,没有很详细的写。只是一个概念的概述ERP实施-委外业务-委外采购业务 最近看PA教材,遇到了这块内容,就再详细的整理一下SAP关于委外的理论知识。 文章目录 概述分包和物料需求计划 (MRP)委外分包订单分包委外业务…...
nlp新词发现——浅析 TF·IDF
传统nlp任务处理文本及其依赖已有的词表,只有在词表里出现的词才能被识别并加以处理。但这也带来了一些问题: 假设没有词表,如何从文本中发现新词? 随着时间推移,新词会不断出现,固有词表会过时࿰…...
WebGL2示例项目常见问题解决方案
WebGL2示例项目常见问题解决方案 webgl2examples Rendering algorithms implemented in raw WebGL 2. [这里是图片001] 项目地址: https://gitcode.com/gh_mirrors/we/webgl2examples 项目基础介绍 WebGL2示例项目(https://github.com/tsherif/webgl2examples.gi…...
鸿蒙元服务从0到上架【第三篇】(第二招有捷径)
第一招:开始发布元服务 AppGallery 上传通过IDE生成的图标,后面按照步骤填写 后台有隐私政策链接生成处,前往填写生成 第二招:用户协议 对于没有服务器或者是需要极速开发的开发者,可通过gitee生成用户协议&…...
Jimureport h2命令执行分析记录
首先找testConnection接口,前面进行了jimureport-spring-boot-starter-1.5.8.jar反编译查找,接口找到发现请求参数是json var1是JmreportDynamicDataSourceVo类型,也就是如上图的dbSource,根据打印的结果可以知道这里是local cac…...
vue 集成 webrtc-streamer 播放视频流 - 解决阿里云内外网访问视频流问题
资料: 史上最详细的webrtc-streamer访问摄像机视频流教程-CSDN博客 webrtc目录 前端集成 html文件夹里的webrtcstreamer.js,集成到前端,可以访问webrtc,转换rtsp为webrtc视频流,在前端video中播放 <videoref&quo…...
进网许可认证、交换路由设备检测项目更新25年1月起
实施时间 2025年1月1日起实施 涉及设备范围 核心路由器、边缘路由器、以太网交换机、三层交换机、宽带网络接入服务器(BNAS) 新增检测依据 GBT41266-2022网络关键设备安全检测方法交换机设备 GBT41267-2022网络关键设备安全技术要求交换机设备 GB/…...
Provides transitive vulnerable dependency maven 提示依赖存在漏洞问题的解决方法
问题描述 如下图所示,对于 java 项目某些依赖,IDEA 提示,引用了含有漏洞的依赖。如果是单个依赖,可以考虑直接升级版本即可。但是对于传递性依赖,比如 flink 项目中,依赖的部分模块,它们自己依…...
WebAuthn 项目常见问题解决方案
WebAuthn 项目常见问题解决方案 webauthn Webauthn / passkeys helper library to make your life easier. Client side, server side and demo included. [这里是图片001] 项目地址: https://gitcode.com/gh_mirrors/webaut/webauthn 项目基础介绍 WebAuthn 项目是一个开源…...
LeetCode 844. 比较含退格的字符串 (C++实现)
1. 题目描述 给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。 注意:如果对空文本输入退格字符,文本继续为空。 示例 1: 输入:s …...
Python8-写一些小作业
记录python学习,直到学会基本的爬虫,使用python搭建接口自动化测试就算学会了,在进阶webui自动化,app自动化 python基础8-灵活运用顺序、选择、循环结构 写一些小练习题目1、给一个半径,求圆的面积和周长,…...
C++ STL vector基本原理和用法
文章目录 基本原理1. 数据存储结构2. 内存管理机制3. 迭代器实现原理4. 元素访问原理5. 插入和删除元素原理 常见用法1. 概述2. 包含头文件3. 定义和初始化4. 常用成员函数5. 迭代器6. 内存管理与性能特点7. 应用场景 基本原理 以下是关于 std::vector 的基本原理讲解…...
【计算机视觉基础CV-图像分类】05 - 深入解析ResNet与GoogLeNet:从基础理论到实际应用
引言 在上一篇文章中,我们详细介绍了ResNet与GoogLeNet的网络结构、设计理念及其在图像分类中的应用。本文将继续深入探讨如何在实际项目中应用这些模型,特别是如何保存训练好的模型、加载模型以及使用模型进行新图像的预测。通过这些步骤,读…...
【人工智能-初级】基于用户的协同过滤推荐算法
文章目录 1. 数据集2. 实验代码3. 代码解释4. 实验结果5. 评估基于用户的协同过滤算法是一种常见的推荐算法,它的核心思想是根据用户之间的相似性来进行推荐。 实验案例: 使用的是电影推荐数据集 MovieLens,实验中我们会通过用户评分数据计算用户之间的相似性,并使用基于用户…...
如何识别钓鱼邮件和诈骗网站?(附网络安全意识培训PPT资料)
识别钓鱼邮件和诈骗网站是网络安全中的一个重要环节。以下是一些识别钓鱼邮件和诈骗网站的方法: 识别钓鱼邮件: 检查发件人地址: 仔细查看发件人的电子邮件地址,看是否与官方域名一致。 检查邮件内容: 留意邮件中是否…...
Rust 在前端基建中的使用
摘要 随着前端技术的不断发展,前端基础设施(前端基建)的建设已成为提升开发效率、保障产品质量的关键环节。然而,在应对复杂业务场景与高性能需求时,传统的前端技术栈逐渐暴露出诸多不足。近年来,Rust语言…...
【人工智能】基于Python和OpenCV实现实时人脸识别系统:从基础到应用
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着人工智能和计算机视觉的快速发展,人脸识别技术已广泛应用于监控、安全、社交媒体、金融和医疗等领域。本文将介绍如何利用Python和Ope…...
Python 自动化 打开网站 填表登陆 例子
图样 简价: 简要说明这个程序的功能: 1. **基本功能**: - 自动打开网站 - 自动填写登录信息(号、公司名称、密码) - 显示半透明状态窗口实时提示操作进度 2. **操作流程**: - 打开网站后自动…...
【Chrome】浏览器提示警告Chrome is moving towards a new experience
文章目录 前言一、如何去掉 前言 Chrome is moving towards a new experience that allows users to choose to browse without third-party cookies. 这是谷歌浏览器(Chrome)关于隐私策略更新相关的提示 提示:以下是本篇文章正文内容&…...
【2026春招必看】MiniMax大模型算法岗面试深度解析:薪资福利+核心考点+项目经验!手把手教你冲刺高薪Offer!
今天给大家梳理出来minmax的福利待遇,顺便分享面试大模型算法岗的面试题。喜欢本文记得收藏、关注、点赞。 废话不多说,我们要开车了。 薪资介绍 月薪(base) 应届/初级(1–2年):35K–50K / 月中…...
DDD难落地?就让AI干吧! - cleanddd-skills介绍秸
AI训练存储选型的演进路线 第一阶段:单机直连时代 早期的深度学习数据集较小,模型训练通常在单台服务器或单张GPU卡上完成。此时直接将数据存储在训练机器的本地NVMe SSD/HDD上。 其优势在于IO延迟最低,吞吐量极高,也就是“数据离…...
记录复现多模态大模型论文OPERA的一周工作()投
pagehelper整合 引入依赖com.github.pagehelperpagehelper-spring-boot-starter2.1.0compile编写代码 GetMapping("/list/{pageNo}") public PageInfo findAll(PathVariable int pageNo) {// 设置当前页码和每页显示的条数PageHelper.startPage(pageNo, 10);// 查询数…...
Span<T>引发的StackOverflowException?揭秘.NET Runtime 7.0中未公开的栈帧校验机制与安全边界(仅限高级开发者)
第一章:Span<T>引发的StackOverflowException现象复现与初步诊断在 .NET Core 3.0 及更高版本中,Span<T> 因其栈上分配特性和零拷贝语义被广泛用于高性能场景。然而,不当的递归使用或跨栈帧传递可能触发 StackOverflowException—…...
别再死磕复杂模型了!用Python+NumPy手把手教你从卫星J2000坐标算出经纬度
从卫星J2000坐标到经纬度:Python实战指南 当拿到卫星的J2000坐标数据时,如何快速将其转换为可在地图上显示的经纬度?本文将用Python和NumPy带你一步步实现这个转换过程,避开复杂的理论推导,专注于代码实现和实际问题解…...
别再只调参了!深入理解PCL网格化:从Delaunay三角剖分到法线一致性的底层原理
深入解析PCL网格化:从Delaunay三角剖分到法线一致性的工程实践 在三维重建领域,点云网格化是将离散采样点转化为连续表面的关键技术。许多开发者虽然能够调用PCL库完成基础网格生成,但当面对setNormalConsistency、setMaximumSurfaceAngle等参…...
第十三节:React Ink——用React驱动终端UI
知识图谱定位:上一节我们拆解了 Task 系统的七种任务类型、五种状态、分布式锁、磁盘安全输出和自动梦境机制——这些是 Agent 在"后台"默默运转的基础设施。但无论后台多强大,用户看不到就白搭。Claude Code 没有选择传统的 console.log 逐行打印,而是用 React 在…...
使用钉钉远程操作你的claude code露
先回顾:三次握手(建立连接)核心流程(实际版) 为了让挥手流程衔接更顺畅,咱们先快速回顾三次握手的实际核心,避免上下文脱节: 第一步(客户端→服务器)…...
C#串口通讯实战:从基础配置到高效数据交互
1. 串口通讯基础与C#实现原理 第一次接触串口通讯时,我盯着那堆参数完全摸不着头脑。后来在工业现场调试传感器时才发现,这东西就像两个人打电话——波特率是语速,数据位是词汇量,停止位就是通话结束时的"再见"。C#中的…...
【车载C#中控系统开发黄金法则】:20年一线专家亲授5大避坑指南与实时响应优化秘技
第一章:车载C#中控系统开发的实时性本质与架构约束车载C#中控系统并非通用桌面应用的简单移植,其核心挑战源于汽车电子环境对确定性响应、资源隔离与功能安全的刚性要求。实时性在此语境下并非指微秒级硬实时(如ECU控制)ÿ…...
