Redis中如何限制操作频率
这篇文章主要介绍Redis中如何限制操作频率,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
场景
场景1
留言功能限制,30秒 内只能评论 10次,超出次数不让能再评论,并提示:过于频繁
场景2
点赞功能限制,10秒 内只能点赞 10次,超出次数后不能再点赞,并禁止操作 1个小时,提示:过于频繁,被禁止操作1小时
场景3
上传记录功能,限制一天只能上传 100次,超出次数不让能再上传,并提示:超出今日上线
抽离本质
在业务开发的过程中,我们不断的参与各种业务场景的方案设计,往往很容易碰到很类似的场景,只不过当前所属的业务模块不一样,其实这些需求的本质是解决同一个问题,当遇到这种场景的时候,我们需要根据自己经验分析抽离出需求的本质问题,实现一个通用的解决方案,让自己的解决方案更有价值,这可能就是区别于你是有灵魂的工程师还是cp(copy paste)最强王者吧。
分析上面3个业务场景,可以从中发现其中有相似的逻辑,称它为同类的问题,现在我们就是要抽离这个问题,设计一个通用的解决方案,勾画相同逻辑流程图:
通过分析上面的需求场景,抽离出他们都需要的那些条件:
限制对象:用户
限制操作(评论,点赞,记录, …)
时间范围X秒内
限制操作数Y次
超出后禁止操作时间Z(秒/具体时间)
超出后不让再操作,并提示
(最小时间单位用秒:天/小时/分钟都可换算成秒,用秒可以解决更多的场景)
如果把功能抽离成一个通用函数是不是大概是这样:
<?php/***频率限制*@paramstring$action操作动作*@paramint$userId发起操作的用户ID*@paramint$time时间范围X秒内*@paramint$number限制操作数Y次*@paramarray$expire超出封印时间Z['type'=>1,'ttl'=>过期时间/秒]['type'=>2,'ttl'=>具体过期时间戳]二选一*@returnbool*@throws\Exception*/publicstaticfunctionfrequencyLimit(string$action,int$userId,int$time,int$number,$expire=[]){//todo根据用户操作动作时间范围,进行频率的控制和失效释放}
解决方案落地
功能中需要对用户发起的操作和时间,以及累计次数进行存储,并且需要失效过期的清理,如果这个时候我们依赖mysql做存储,想想都觉的挺痛苦,这里主角:redis 终于登场了,基于redis特性,incr的原子操作和key 支持过期机制,内存存储的效率优势,可以相对简单灵活并且又高效的完成目的。
这里简单实现个通用功能的代码:
<?php/***频率限制*@paramstring$action操作动作*@paramint$userId发起操作的用户ID*@paramint$time时间范围X秒内*@paramint$number限制操作数Y次*@paramarray$expire超出封印时间Z['type'=>1,'ttl'=>过期时间/秒]['type'=>2,'ttl'=>具体过期时间戳]二选一*@returnbool*@throws\Exception*/publicfunctionfrequencyLimit(string$action,int$userId,int$time,int$number,$expire=[]){if(empty($action)||$userId<=0||$time<=0||$number<=0){thrownew\Exception('非法参数');}$key='act:limit:'.$action.':'.$userId;$r=RedisClient::connect();//获取当前累计次数$current=intval($r->get($key));if($current>=$number)returnfalse;//累计并返回最新值$current=$r->incr($key);//第一次累加,设置控制操作频率的有效时间if($current===1)$r->expire($key,$time);//未超出限制次数先放过if($current<$number)returntrue;//超出后根据需要重新设置过期失效时间$current===$number判断保证只重新设置一次$type=empty($expire['type'])?0:intval($expire['type']);$ttl=empty($expire['ttl'])?0:intval($expire['ttl']);if($current===$number&&$ttl>0&&in_array($type,[1,2])){if($type===1)$r->expire($key,$ttl);if($type===2)$r->expireAt($key,$ttl);}returnfalse;}//场景1/***评论限制*@paramint$userId*@returnbool|string*/publicfunctiondoComment(int$userId){try{$pass=FrequencyLimit::doHandle('comment',$userId,30,10);if(!$pass)return'过于频繁';//todo评论逻辑returntrue;}catch(\Exception$e){return$e->getMessage();}}//场景2/***点赞限制*@paramint$userId*@returnbool|string*/publicfunctiondoLike(int$userId){try{$pass=FrequencyLimit::doHandle('like',$userId,10,10,['type'=>1,'ttl'=>1*60*60]);if(!$pass)return'过于频繁,被禁止操作1小时';//todo点赞逻辑returntrue;}catch(\Exception$e){return$e->getMessage();}}//场景3/***上传限制*@paramint$userId*@returnbool|string*/publicfunctiondoUpload(int$userId){try{$expire=strtotime(date('Y-m-d',strtotime(+1.'days')));$pass=FrequencyLimit::doHandle('upload',$userId,1*24*60*60,100,['type'=>2,'ttl'=>$expire]);if(!$pass)return'超出今日上线';//todo上传逻辑returntrue;}catch(\Exception$e){return$e->getMessage();}}//场景N
编码上可以根据你设计这个通用方案的复杂度进行进一步抽象,如抽象成频率限制的功能类 等
以上是“Redis中如何限制操作频率”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。