市面上很多介绍redis如何实现限流的,但是大部分都有一个缺点,就是只能实现单一的限流,比如1分钟访问1次或者60分钟访问10次这种,但是如果想一个接口两种规则都需要满足呢 , 我们的项目又是分布式项目,应该如何解决 , 下面就介绍一下redis实现分布式多规则限流的方式 。
简介市面上很多介绍redis如何实现限流的,但是大部分都有一个缺点,就是只能实现单一的限流 , 比如1分钟访问1次或者60分钟访问10次这种,但是如果想一个接口两种规则都需要满足呢,我们的项目又是分布式项目,应该如何解决,下面就介绍一下redis实现分布式多规则限流的方式 。
思考
- 如何一分钟只能发送一次验证码,一小时只能发送10次验证码等等多种规则的限流
- 如何防止接口被恶意打击(短时间内大量请求)
- 如何限制接口规定时间内访问次数
记录某IP访问次数使用 String结构 记录固定时间段内某用户IP访问某接口的次数
- RedisKey = prefix : className : methodName
- RedisVlue = 访问次数
- 初次访问时设置 「[RedisKey] [RedisValue=https://www.isolves.com/it/sjk/Redis/2024-01-03/1] [规定的过期时间]」
- 获取 RedisValue 是否超过规定次数,超过则拦截,未超过则对 RedisKey 进行加1
- 考虑并发问题
目前大量请求进行到第一步( 获取Redis请求次数 ),那么所有线程都获取到了值为999,进行判断都未超过限定次数则不拦截,导致实际次数超过 1000 次
「解决办法:」 保证方法执行原子性(加锁、lua)
- 考虑在临界值进行访问
- 思考下图
文章插图
图片
代码实现: 比较简单
【Redis 实现多规则限流的思考与实践】参考:https://gitee.com/y_project/RuoYi-Vue/blob/master/ruoyi-framework/src/main/JAVA/com/ruoyi/framework/aspectj/RateLimiterAspect.java 。
Zset解决临界值问题使用 Zset 进行存储,解决临界值访问问题
文章插图
图片
网上几乎都有实现,这里就不过多介绍
实现多规则限流
先确定最终需要的效果
- 能实现多种限流规则
- 能实现防重复提交
@RateLimiter(rules = {// 60秒内只能访问10次@RateRule(count = 10, time = 60, timeUnit = TimeUnit.SECONDS),// 120秒内只能访问20次@RateRule(count = 20, time = 120, timeUnit = TimeUnit.SECONDS)},// 防重复提交 (5秒钟只能访问1次)preventDuplicate = true)
编写注解(RateLimiter,RateRule)编写 RateLimiter 注解 。/** * @Description: 请求接口限制 * @Author: yiFei */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface RateLimiter {/*** 限流key*/String key() default RedisKeyConstants.RATE_LIMIT_CACHE_PREFIX;/*** 限流类型 ( 默认 Ip 模式 )*/LimitTypeEnum limitType() default LimitTypeEnum.IP;/*** 错误提示*/ResultCode message() default ResultCode.REQUEST_MORE_ERROR;/*** 限流规则 (规则不可变,可多规则)*/RateRule[] rules() default {};/*** 防重复提交值*/boolean preventDuplicate() default false;/*** 防重复提交默认值*/RateRule preventDuplicateRule() default @RateRule(count = 1, time = 5);}
编写RateRule注解@Target(ElementType.ANNOTATION_TYPE)@Retention(RetentionPolicy.RUNTIME)@Inheritedpublic @interface RateRule {/*** 限流次数*/long count() default 10;/*** 限流时间*/long time() default 60;/*** 限流时间单位*/TimeUnit timeUnit() default TimeUnit.SECONDS;}
拦截注解 RateLimiter- 确定redis存储方式
RedisScore = 时间戳
RedisValue = https://www.isolves.com/it/sjk/Redis/2024-01-03/任意分布式不重复的值即可
推荐阅读
- 如何用Java实现自动化测试和质量控制?
- 龙年春晚彩排骂声一片!多位混子演员现身央视,评论区炸了
- 游戏不能进行多开怎么设置,如何解除游戏多开限制
- 长城的长度约为多少? 长城的长度约为多少
- 小学必学多音字大全 小学生考试必知的50个多音字
- 贝克汉姆晒家庭聚餐,12岁爱女脱离稚嫩,如今比妈妈维多利亚更美
- 1949年属什么生肖今年多大 1949年属什么生肖
- 胡歌携妻子出演《繁花》:胡歌那么多“前女友”,为何偏偏娶她?
- 脚手架一步一跨是多少,脚手架的水平杆的步距是多少
- 骨折后怎么吃比较好