Redis实现限流器的方法有哪些
这篇文章主要介绍了Redis实现限流器的方法有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Redis实现限流器的方法有哪些文章都会有所收获,下面我们一起来看看吧。
方法一:基于Redis的setnx的操作我们在使用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 :
publicclassRequestCountInterceptorimplementsHandlerInterceptor{privateLimitPolicylimitPolicy;publicRequestCountInterceptor(LimitPolicylimitPolicy){this.limitPolicy=limitPolicy;}@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if(!limitPolicy.canDo()){returnfalse;}returntrue;}}
同时添加一个配置LimitConfiguration:
@ConfigurationpublicclassLimitConfigurationimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newRequestCountInterceptor(newRedisLimit1())).addPathPatterns("/my/increase");}}
这样每次在/my/increase请求到达Controller之前按策略RedisLimit1进行限流,原先Controller里面的代码就不用修改了:
@RestController@RequestMapping("my")publicclassMyController{inti=0;@RequestMapping("/increase")publicintincrease(){returni++;}}
具体的限流逻辑代码是在RedisLimit1类中:
/***方法一:基于Redis的setnx的操作*/publicclassRedisLimit1extendsLimitPolicy{static{setNxExpire();}privatestaticbooleansetNxExpire(){SetParamssetParams=newSetParams();setParams.nx();setParams.px(TIME);Stringresult=jedis.set(KEY,COUNT+"",setParams);if(SUCCESS.equals(result)){returntrue;}returnfalse;}@OverridepublicbooleancanDo(){if(setNxExpire()){//设置成功,说明原先不存在,成功设置为COUNTreturntrue;}else{//设置失败,说明已经存在,直接减1,并且返回returnjedis.decrBy(KEY,1)>0;}}}publicabstractclassLimitPolicy{publicstaticfinalintCOUNT=10;//10requestpublicstaticfinalintTIME=10*1000;//10spublicstaticfinalStringSUCCESS="OK";staticJedisjedis=newJedis();abstractbooleancanDo();}
这样实现的一个效果是每秒最多请求10次。
方法二:基于Redis的数据结构zset其实限流涉及的最主要的就是滑动窗口,上面也提到1-10怎么变成2-11。其实也就是起始值和末端值都各+1即可。
而我们如果用Redis的list数据结构可以轻而易举的实现该功能
我们可以将请求打造成一个zset数组,当每一次请求进来的时候,value保持唯一,可以用UUID生成,而score可以用当前时间戳表示,因为score我们可以用来计算当前时间戳之内有多少的请求数量。而zset数据结构也提供了zrange方法让我们可以很轻易的获取到2个时间戳内有多少请求
/***方法二:基于Redis的数据结构zset*/publicclassRedisLimit2extendsLimitPolicy{publicstaticfinalStringKEY2="LIMIT2";@OverridepublicbooleancanDo(){LongcurrentTime=newDate().getTime();System.out.println(currentTime);if(jedis.zcard(KEY2)>0){//这里不能用get判断,会报错:WRONGTYPEOperationagainstakeyholdingthewrongkindofvalueIntegercount=jedis.zrangeByScore(KEY2,currentTime-TIME,currentTime).size();//注意这里使用zrangeByScore,以时间作为score。zrangekeystartstop命令的start和stop是序号。System.out.println(count);if(count!=null&&count>COUNT){returnfalse;}}jedis.zadd(KEY2,Double.valueOf(currentTime),UUID.randomUUID().toString());returntrue;}}
通过上述代码可以做到滑动窗口的效果,并且能保证每N秒内至多M个请求,缺点就是zset的数据结构会越来越大。实现方式相对也是比较简单的。
方法三:基于Redis的令牌桶算法提到限流就不得不提到令牌桶算法了。令牌桶算法提及到输入速率和输出速率,当输出速率大于输入速率,那么就是超出流量限制了。也就是说我们每访问一次请求的时候,可以从Redis中获取一个令牌,如果拿到令牌了,那就说明没超出限制,而如果拿不到,则结果相反。
依靠上述的思想,我们可以结合Redis的List数据结构很轻易的做到这样的代码,只是简单实现 依靠List的leftPop来获取令牌。
首先配置一个定时任务,通过redis的list的rpush方法每秒插入一个令牌:
@Configuration//1.主要用于标记配置类,兼备Component的效果。@EnableScheduling//2.开启定时任务publicclassSaticScheduleTask{//3.添加定时任务@Scheduled(fixedRate=1000)privatevoidconfigureTasks(){LimitPolicy.jedis.rpush("LIMIT3",UUID.randomUUID().toString());}}
限流时,通过list的lpop方法从redis中获取对应的令牌,如果获取成功表明可以执行请求:
/***方法三:令牌桶*/publicclassRedisLimit3extendsLimitPolicy{publicstaticfinalStringKEY3="LIMIT3";@OverridepublicbooleancanDo(){Objectresult=jedis.lpop(KEY3);if(result==null){returnfalse;}returntrue;}}
关于“Redis实现限流器的方法有哪些”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Redis实现限流器的方法有哪些”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。