Redis实现限流器的方法有哪些

网友投稿 534 2023-11-22

Redis实现限流器的方法有哪些

这篇文章主要介绍了Redis实现限流器的方法有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Redis实现限流器的方法有哪些文章都会有所收获,下面我们一起来看看吧。

方法一:基于Redis的setnx的操作

Redis实现限流器的方法有哪些

我们在使用Redis的分布式锁的时候,大家都知道是依靠了setnx的指令,在CAS(Compare and swap)的操作的时候,同时给指定的key设置了过期实践(expire),我们在限流的主要目的就是为了在单位时间内,有且仅有N数量的请求能够访问我的代码程序。所以依靠setnx可以很轻松的做到这方面的功能。

比如我们需要在10秒内限定20个请求,那么我们在setnx的时候可以设置过期时间10,当请求的setnx数量达到20时候即达到了限流效果。代码比较简单就不做展示了。

当然这种做法的弊端是很多的,比如当统计1-10秒的时候,无法统计2-11秒之内,如果需要统计N秒内的M个请求,那么我们的Redis中需要保持N个key等等问题。

在具体实现的时候,可以考虑使用-HandlerInterceptor :

public class RequestCountInterceptor implements HandlerInterceptor {     private LimitPolicy limitPolicy;     public RequestCountInterceptor(LimitPolicy limitPolicy) {         this.limitPolicy = limitPolicy;     }     @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throwsException{         if (!limitPolicy.canDo()) {             return false;         }         return true;     } }

同时添加一个配置LimitConfiguration:

@Configuration public class LimitConfiguration implements WebMvcConfigurer {     @Override     public void addInterceptors(InterceptorRegistry registry) {         registry.addInterceptor(new RequestCountInterceptor(new RedisLimit1())).addPathPatterns("/my/increase");     } }

这样每次在/my/increase请求到达Controller之前按策略RedisLimit1进行限流,原先Controller里面的代码就不用修改了:

@RestController @RequestMapping("my") public class MyController {     int i = 0;     @RequestMapping("/increase")     public int increase() {         return i++;     } }

具体的限流逻辑代码是在RedisLimit1类中:

/** * 方法一:基于Redis的setnx的操作 */ public class RedisLimit1 extends LimitPolicy {     static{         setNxExpire();     }private static boolean setNxExpire() {         SetParams setParams = newSetParams();         setParams.nx();         setParams.px(TIME);         String result = jedis.set(KEY, COUNT +"", setParams);         if (SUCCESS.equals(result)) {             return true;         }         return false;     }@Override     public boolean canDo() {         if (setNxExpire()) {             //设置成功,说明原先不存在,成功设置为COUNT             return true;         }else {             //设置失败,说明已经存在,直接减1,并且返回             return jedis.decrBy(KEY, 1) > 0;         }     } }public abstract class LimitPolicy {     public static final int COUNT = 10//10 request     public static final int TIME= 10*1000 ; // 10s     public static final String SUCCESS = "OK";     static Jedis jedis = new Jedis();     abstract boolean canDo(); }

这样实现的一个效果是每秒最多请求10次。

方法二:基于Redis的数据结构zset

其实限流涉及的最主要的就是滑动窗口,上面也提到1-10怎么变成2-11。其实也就是起始值和末端值都各+1即可。

而我们如果用Redis的list数据结构可以轻而易举的实现该功能

我们可以将请求打造成一个zset数组,当每一次请求进来的时候,value保持唯一,可以用UUID生成,而score可以用当前时间戳表示,因为score我们可以用来计算当前时间戳之内有多少的请求数量。而zset数据结构也提供了zrange方法让我们可以很轻易的获取到2个时间戳内有多少请求/** * 方法二:基于Redis的数据结构zset */ public class RedisLimit2 extends LimitPolicy {     public static final String KEY2 = "LIMIT2";     @Override     public boolean canDo() {         Long currentTime = new Date().getTime();System.out.println(currentTime);         if (jedis.zcard(KEY2) > 0) { // 这里不能用get判断,会报错:WRONGTYPE Operation against a key holding the wrong kind of value             Integer count = jedis.zrangeByScore(KEY2, currentTime -TIME, currentTime).size(); // 注意这里使用zrangeByScore,以时间作为score。zrange key start stop 命令的start和stop是序号。             System.out.println(count);             if (count != null && count > COUNT) {                 return false;             }         }         jedis.zadd(KEY2Double.valueOf(currentTime), UUID.randomUUID().toString());return true;     } }

通过上述代码可以做到滑动窗口的效果,并且能保证每N秒内至多M个请求,缺点就是zset的数据结构会越来越大。实现方式相对也是比较简单的。

方法三:基于Redis的令牌桶算法

提到限流就不得不提到令牌桶算法了。令牌桶算法提及到输入速率和输出速率,当输出速率大于输入速率,那么就是超出流量限制了。也就是说我们每访问一次请求的时候,可以从Redis中获取一个令牌,如果拿到令牌了,那就说明没超出限制,而如果拿不到,则结果相反。

依靠上述的思想,我们可以结合Redis的List数据结构很轻易的做到这样的代码,只是简单实现 依靠List的leftPop来获取令牌。

首先配置一个定时任务,通过redis的list的rpush方法每秒插入一个令牌:

@Configuration      //1.主要用于标记配置类,兼备Component的效果。 @EnableScheduling   // 2.开启定时任务 public class SaticScheduleTask {     //3.添加定时任务     @Scheduled(fixedRate = 1000)     private void configureTasks() {LimitPolicy.jedis.rpush("LIMIT3", UUID.randomUUID().toString());     } }

限流时,通过list的lpop方法从redis中获取对应的令牌,如果获取成功表明可以执行请求:

/** * 方法三:令牌桶 */ public class RedisLimit3 extends LimitPolicy {     public static final String KEY3 = "LIMIT3";     @Override     public boolean canDo() {         Object result = jedis.lpop(KEY3);         if (result == null) {             return false;         }         return true;     } }

关于“Redis实现限流器的方法有哪些”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Redis实现限流器的方法有哪些”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注行业资讯频道。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SQL窗口函数之排名窗口函数怎么使用
下一篇:mysql中delete in子查询不走索引问题怎么解决
相关文章

 发表评论

暂时没有评论,来抢沙发吧~