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

令牌桶

时间:2023-03-29 16:08:06 PHP

高可用性对于应用程序和API接口至关重要。如果我们提供一个接口,突然面临流量的爆发式增长,在这种情况下,不仅会影响网站的访问速度,甚至可能导致服务器崩溃,导致所有用户无法正常访问。对于这种情况,有同学认为:“我们可以通过提高配置或者增加机器来解决这个问题。”在某些情况下这确实是一种选择。但是,我们在使用一个接口或者应用的时候,不仅需要通过技术手段(幂等性、熔断等)来提高它们的稳定性,还要保证不会因为其他意想不到的原因(比如新同事写的代码)而发生意外。等)问题。在以下情况下,频率限制可以帮助我们更好地保证API的可用性:用户使用脚本向我们发送大量请求。用户向我们发送了很多低优先级接口数据的请求,我们希望这些低优先级的请求尽量不要影响到我们高优先级的接口请求(比如电商,一定要保证订单处理正常,其他接口的优先级自然低于订单流程相关的接口)。由于突发原因,所有接口不能同时正常访问,所以需要暂时丢弃优先级低的请求(当然这在Nginx层面也是可以的)。令牌桶令牌桶算法的原理是系统将令牌以恒定的速度放入桶中,如果需要处理请求,则需要先从桶中取出令牌。当桶中没有可用的令牌时,拒绝服务(拒绝服务)。特点:当桶满时,不再放置令牌,即令牌数量不会超过桶的最大容量。由于token是有一段时间限制的,即使突发流量也能很好的保护服务。实现方式令牌桶一般有两种常用的实现方式:第一种是在后台启动一个线程,按照一定的时间粒度不断向固定大小的桶(bucket)中添加令牌,直到达到最大容量为止桶。这种方法不仅实现起来有点麻烦,而且还需要额外维护一个脚本;并且在没有请求的情况下,线程会继续检查和更新token。如果key很多,对CPU的性能影响会比较大。.第二种(本文案例)每次访问时将本次访问的时间和速率存储在redis中,下一次新请求访问时,比较当前时间与上次请求时间的时间差的可用token个数,并将新结果存储在redis中。代码实现initNumbucketinitialsizeexpireunittimenowTime当前访问时间limitData['time']最后请求时间redis=\Yii::$app->redis;$this->initNum=$initNum??$this->initNum;$this->expire=$expire??$this->过期;$this->cacheKey=$cacheKey;}/***handler*@returnarray*/publicfunctionhandler(){$ret=self::_limit($this->cacheKey,$this->initNum,$this->过期);如果(empty($ret['status'])){返回false;}返回真;}privatefunction_limit($cacheKey='',$initNum='',$expire=''){$nowTime=time();$this->redis->watch($cacheKey);$redisData=$this->redis->get($cacheKey);$limitData=$redisData?json_decode($redisData,true):['num'=>$initNum,'time'=>$nowTime];//(单位时间的访问频率/单位时间)*(当前时间-上次访问时间)=自上次请求后可以增加访问次数$addNum=($initNum/$expire)*($nowTime-$limitData['time']);$newNum=min($initNum,(($limitData['num']-1)+$addNum));if($newNum<=0){return['status'=>false,'msg'=>'此时令牌已用完!'];}$limitData=json_encode(['num'=>$newNum,'time'=>$nowTime]);$this->redis->multi();$this->redis->set($cacheKey,$limitData);如果(!$this->redis->exec()){return['status'=>false,'msg'=>'访问次数过多!'];}返回['status'=>true,'msg'=>'ok'];}}总结Token桶限频是API接口设计中重要的安全策略之一,但是针对不同的业务和场景,应该采用最合适的方式(比如登录密码错误,一天只能尝试五次),在这种情况下,计数器频率限制是一个更好的选择),一切都没有绝对的参考https://medium.com/smyte/rate-limiter-df3408325846https://stripe.com/en-hk/blog/速率限制器