当前位置: 首页 > 后端技术 > PHP

实战中限制Redis的运行频率

时间:2023-03-29 13:55:05 PHP

最近业务开发沉迷不能自拔?好久没更新博文了。后续博文内容计划分享一些业务场景下的实用解决方案或者更好的设计思路。和之前一样,围绕一个话题整理相关内容花了很多时间(大招),后续文章的内容可能不会那么丰富,但尽量集中在一个点上,或者进行更深入的分析。通过不断的分享和自我检讨,积累经验,增加博文分享频率?场景一留言功能受限,30秒内只能发表10条评论。超过点赞次数将不允许再评论,并提醒:场景过于频繁2点赞功能限制,10秒内只能点赞10次,超过点赞次数后,不能再点赞,并禁止操作1小时,提示:过于频繁,禁止操作1小时场景3上传记录功能,仅限一天可上传100次,超过次数不允许上传时代,并提醒它超越了今天在线脱离的本质。在业务发展的过程中,我们不断参与各种业务场景的设计,往往容易遇到非常相似的场景,只是目前的业务模块不同。其实这些需求的本质都是为了解决同一个问题。遇到这样的场景,我们需要根据自己的经验,分析提取需求的本质,实现通用的解决方案。解决方案让自己的解决方案更有价值,这可能就是你是有灵魂的工程师还是cp之王(复制粘贴)的区别。分析以上三个业务场景,我们可以发现它们之间存在相似的逻辑,称之为相似问题。现在我们要抽取这个问题,设计一个通用的解决方案,画出相同的逻辑流程图:通过分析上面的需求场景,抽取他们都需要的条件:限制对象:用户限制操作(评论,点赞,记录,...)在时间范围X秒内限制操作次数Y次,然后禁止操作时间Z(秒/具体时间)超过则不允许再次操作,并提示(最小时间单位是秒:天/小时/分钟可以换算成秒,用秒来解决更多的场景)可不可以把函数提取成一个通用的函数?是这样的:1,'ttl'=>expirationtime/second]['type'=>2,'ttl'=>具体expiretimestamp]2选择一个*@returnbool*@throws\Exception*/publicstaticfunctionfrequencyLimit(string$action,int$userId,int$time,int$number,$expire=[]){//todoactiontime范围根据用户操作,频率控制和失败释放}解决方案登陆功能需要存储用户发起的操作和时间,以及累计次数,需要清除过期和过期。如果这个时候我们依赖mysql来存储,想想就会觉得挺蛋疼的,这里是主角:redis终于登场了,基于redis的特性,incr的原子操作和key支持过期机制,以及内存存储的效率优势,可以相对简单、灵活、高效地达到目的。下面是一个简单的代码实现一个通用的功能: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:'.$行动。':'。$用户名;$r=RedisClient::connect();//获取当前累计数$current=intval($r->get($key));如果($current>=$number)返回false;//累加并返回最新的值$current=$r->incr($key);//先累加,设置有效时间控制运行频率if($current===1)$r->expire($key,$time);//如果没有超过限制,先放手if($current<$number)returntrue;//超过限制后,根据需要重新设置过期时间$current===$number保证只重启一次$type=empty($expire['type'])?0:intval($过期[‘类型’]);$ttl=empty($expire['ttl'])?0:整数($expire['ttl']);如果($current===$number&&$ttl>0&&in_array($type,[1,2])){如果($type===1)$r->expire($key,$ttl);如果($type===2)$r->expireAt($key,$ttl);}returnfalse;}//场景一/***评论限制*@paramint$userId*@returnbool|string*/publicfunctiondoComment(int$userId){try{$pass=FrequencyLimit::doHandle('comment',$userId,30,10);如果(!$pass)返回“太频繁”;//todo注释逻辑returntrue;}catch(\Exception$e){返回$e->getMessage();}}//场景2/***Likelimit*@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){返回$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){返回$e->getMessage();}}//场景N编码可以根据你设计这个通用方案的复杂程度进一步抽象,比如抽象成频率受限的功能类等。总结分析类似的业务场景,找到本质问题,设计通用的。解决方案使解决方案更有价值。做一个有灵魂的开发者,掌握redis,充分利用它的特性和优势。首次发布在Github上?聊聊WEB开发,欢迎Star?