Redis 实现多规则限流的思考与实践

市面上很多介绍redis如何实现限流的,但是大部分都有一个缺点,就是只能实现单一的限流,比如1分钟访问1次或者60分钟访问10次这种,但是如果想一个接口两种规则都需要满足呢 , 我们的项目又是分布式项目,应该如何解决 , 下面就介绍一下redis实现分布式多规则限流的方式 。 
简介市面上很多介绍redis如何实现限流的,但是大部分都有一个缺点,就是只能实现单一的限流 , 比如1分钟访问1次或者60分钟访问10次这种,但是如果想一个接口两种规则都需要满足呢,我们的项目又是分布式项目,应该如何解决,下面就介绍一下redis实现分布式多规则限流的方式 。
思考

  1. 如何一分钟只能发送一次验证码,一小时只能发送10次验证码等等多种规则的限流
  2. 如何防止接口被恶意打击(短时间内大量请求)
  3. 如何限制接口规定时间内访问次数
解决方法 
记录某IP访问次数使用 String结构 记录固定时间段内某用户IP访问某接口的次数
  • RedisKey = prefix : className : methodName
  • RedisVlue = 访问次数
拦截请求:
  1. 初次访问时设置 「[RedisKey] [RedisValue=https://www.isolves.com/it/sjk/Redis/2024-01-03/1] [规定的过期时间]」
  2. 获取 RedisValue 是否超过规定次数,超过则拦截,未超过则对 RedisKey 进行加1
分析: 规则是每分钟访问 1000 次
  1. 考虑并发问题
假设目前 RedisKey => RedisValue 为 999
目前大量请求进行到第一步( 获取Redis请求次数 ),那么所有线程都获取到了值为999,进行判断都未超过限定次数则不拦截,导致实际次数超过 1000 次
「解决办法:」 保证方法执行原子性(加锁、lua)
  1. 考虑在临界值进行访问
  • 思考下图

Redis 实现多规则限流的思考与实践

文章插图
图片
代码实现: 比较简单
【Redis 实现多规则限流的思考与实践】参考:https://gitee.com/y_project/RuoYi-Vue/blob/master/ruoyi-framework/src/main/JAVA/com/ruoyi/framework/aspectj/RateLimiterAspect.java 。
Zset解决临界值问题使用 Zset 进行存储,解决临界值访问问题
Redis 实现多规则限流的思考与实践

文章插图
图片
网上几乎都有实现,这里就不过多介绍
实现多规则限流 
先确定最终需要的效果
  • 能实现多种限流规则
  • 能实现防重复提交
通过以上要求设计注解(先想象出最终实现效果)
@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存储方式
RedisKey = prefix : className : methodName
RedisScore = 时间戳
RedisValue = https://www.isolves.com/it/sjk/Redis/2024-01-03/任意分布式不重复的值即可


推荐阅读