首页 » PHP教程 » phpapi限制技巧_PHP是若何对API进行限流的

phpapi限制技巧_PHP是若何对API进行限流的

访客 2024-11-05 0

扫一扫用手机浏览

文章目录 [+]

通过限流,我们可以很好地掌握系统的qps,从而达到保护系统或者接口做事器稳定的目的。

接口限流的常用算法计数器法计数器法是限流算法里最大略也是最随意马虎实现的一种算法。

phpapi限制技巧_PHP是若何对API进行限流的

比如我们规定,对付A接口来说,我们1分钟的访问次数不能超过100个。
那么我们可以这么做:在一开始的时候,我们可以设置一个计数器counter,每当一个要求过来的时候,counter就加1,如果counter的值大于100并且该要求与第一个要求的间隔韶光还在1分钟之内,那么解释要求数过多;

phpapi限制技巧_PHP是若何对API进行限流的
(图片来自网络侵删)

如果该要求与第一个要求的间隔韶光大于1分钟,且counter的值还在限流范围内,那么就重置counter。

详细算法的示意图如下:

代码如下

classCounterDemo{private$first_request_time;private$request_count=0;//已要求的次数public$limit=100;//韶光窗口内的最大要求数public$interval=60;//韶光窗口spublicfunction__construct(){$this->first_request_time=time();}publicfunctiongrant(){$now=time();if($now<$this->first_request_time+$this->interval){//韶光窗口内if($this->request_count<$this->limit){$this->request_count++;returntrue;}else{returnfalse;}}else{//超出前一个韶光窗口后,重置第一次要求韶光和要求总次数$this->first_request_time=$now;$this->request_count=1;returntrue;}}}$m=newCounterDemo();$n_success=0;for($i=0;$i<200;$i++){$rt=$m->grant();if($rt){$n_success++;}}echo'成功要求'.$n_success.'次';

计数器算法很大略,但是有个严重的bug:

一个恶意用户在0:59时瞬间发送了100个要求,然后再1:00时又瞬间发送了100个要求,那么这个用户在2秒内发送了200个要求。

上面我们规定1分钟最多处理100个要求, 也便是每秒1.7个要求。
用户通过在韶光窗口的重置节点处突发要求, 可以瞬间超过系统的承载能力,导致系统挂起或宕机。

上面的问题,实在是由于我们统计的精度太低造成的。
那么如何很好地处理这个问题呢?或者说,如何将临界问题的影响降落呢?我们可以看下面的滑动窗口算法。

上图中,我们把一个韶光窗口(一分钟)分成6份,每份(小格)代表10秒。
每过10秒钟我们就把韶光窗口往右滑动一格, 每一个格子都有自己独立的计数器。

比如一个要求在0:35秒到达的时候,就会落在0:30-0:39这个区间,并将此区间的计数器加1。

从上图可以看出, 0:59到达的100个要求会落在0:50-0:59这个灰色的格子中, 而1:00到达的100个要求会落在黄色的格子中。

而在1:00韶光统计时, 窗口会往右移动一格,那么此时的韶光窗口内的要求数量一共是200个,超出了限定的100个,触发了限流,后面的100个要求被抛弃或者等待。

如果我们把窗口韶光划分越多, 比如60格,每格1s, 那么限流统计会更精确。

漏桶算法 (Leaky Bucket)

漏桶算法(Leaky Bucket): 平滑网络上的突发流量。
使其整流为一个稳定的流量。

有一个固定容量的桶,有水流进来,也有水流出 去。
对付流进来的水来说,我们无法估量一共有多少水会流进来,也无法估量水流的速率。
但是对付流出去的水来说,这个桶可以固定水流出的速率。
当桶满了之后,多余的水将会溢出(多余的要求会被丢弃)。

大略的算法实现:

classLeakyBucketDemo{private$last_req_time;//上一次要求的韶光public$capacity;//桶的容量public$rate;//水漏出的速率(个/秒)public$water;//当前水量(当前累积要求数)publicfunction__construct(){$this->last_req_time=time();$this->capacity=100;$this->rate=20;$this->water=0;}publicfunctiongrant(){$now=time();$water=max(0,$this->water-($now-$this->last_req_time)$this->rate);//先实行漏水,打算剩余水量$this->water=$water;$this->last_req_time=$now;if($water<$this->capacity){//考试测验加水,并且水还未满$this->water+=1;returntrue;}else{//水满,谢绝加水returnfalse;}}}$m=newLeakyBucketDemo();$n_success=0;for($i=0;$i<500;$i++){$rt=$m->grant();if($rt){$n_success++;}if($i>0&&$i%100==0){//每发起100次后停息1secho'已发送',$i,',成功',$n_success,',sleep'.PHP_EOL;sleep(1);}}echo'成功要求'.$n_success.'次';

令牌桶算法 (Token Bucket)

令牌桶算法比漏桶算法稍显繁芜。
首先,我们有一个固定容量的桶,桶里存放着令牌(token)。
桶一开始是空的(可用token数为0),token以一个固定的速率r往桶里添补,直到达到桶的容量,多余的令牌将会被丢弃。
每当一个要求过来时,就会考试测验从桶里移除一个令牌,如果没有令牌的话,要求无法通过。

代码实现如下:

classTokenBucketDemo{private$last_req_time;//上次要求韶光public$capacity;//桶的容量public$rate;//令牌放入的速率(个/秒)public$tokens;//当前可用令牌的数量publicfunction__construct(){$this->last_req_time=time();$this->capacity=100;$this->rate=20;$this->tokens=100;//开始给100个令牌}publicfunctiongrant(){$now=time();$tokens=min($this->capacity,$this->tokens+($now-$this->last_req_time)$this->rate);//打算桶里可用的令牌数$this->tokens=$tokens;$this->last_req_time=$now;if($this->tokens<1){//若剩余不到1个令牌,则谢绝returnfalse;}else{//还有令牌,领取1个令牌$this->tokens-=1;returntrue;}}}$m=newTokenBucketDemo();$n_success=0;for($i=0;$i<500;$i++){$rt=$m->grant();if($rt){$n_success++;}if($i>0&&$i%100==0){//每发起100次后停息1secho'已发送',$i,',成功',$n_success,',sleep'.PHP_EOL;sleep(1);}}echo'成功要求'.$n_success.'次';

我们可以利用redis的行列步队作为令牌桶容器利用,利用lPush(入队),rPop(出队),实现令牌加入与花费的操作。
TokenBucket.php

<?php/ PHP基于Redis利用令牌桶算法实现接口限流,利用redis的行列步队作为令牌桶容器,入队(lPush)出队(rPop)作为令牌的加入与花费操作。
publicadd加入令牌publicget获取令牌publicreset重设令牌桶privateconnect创建redis连接/classTokenBucket{//classstartprivate$_config;//redis设定private$_redis;//redis工具private$_queue;//令牌桶private$_max;//最大令牌数/初始化@paramArray$configredis连接设定/publicfunction__construct($config,$queue,$max){$this->_config=$config;$this->_queue=$queue;$this->_max=$max;$this->_redis=$this->connect();}/加入令牌@paramInt$num加入的令牌数量@returnInt加入的数量/publicfunctionadd($num=0){//当前剩余令牌数$curnum=intval($this->_redis->lSize($this->_queue));//最大令牌数$maxnum=intval($this->_max);//打算最大可加入的令牌数量,不能超过最大令牌数$num=$maxnum>=$curnum+$num?$num:$maxnum-$curnum;//加入令牌if($num>0){$token=array_fill(0,$num,1);$this->_redis->lPush($this->_queue,...$token);return$num;}return0;}/获取令牌@returnBoolean/publicfunctionget(){return$this->_redis->rPop($this->_queue)?true:false;}/重设令牌桶,填满令牌/publicfunctionreset(){$this->_redis->delete($this->_queue);$this->add($this->_max);}/创建redis连接@returnLink/privatefunctionconnect(){try{$redis=newRedis();$redis->connect($this->_config['host'],$this->_config['port'],$this->_config['timeout'],$this->_config['reserved'],$this->_config['retry_interval']);if(empty($this->_config['auth'])){$redis->auth($this->_config['auth']);}$redis->select($this->_config['index']);}catch(RedisException$e){thrownewException($e->getMessage());returnfalse;}return$redis;}}?>

令牌的如果与花费

<?php/演示令牌加入与花费/require'TokenBucket.php';//redis连接设定$config=array('host'=>'localhost','port'=>6379,'index'=>0,'auth'=>'','timeout'=>1,'reserved'=>NULL,'retry_interval'=>100,);//令牌桶容器$queue='mycontainer';//最大令牌数$max=5;//创建TrafficShaper工具$tokenBucket=newTokenBucket($config,$queue,$max);//重设令牌桶,填满令牌$tokenBucket->reset();//循环获取令牌,令牌桶内只有5个令牌,因此末了3次获取失落败for($i=0;$i<8;$i++){var_dump($tokenBucket->get());}//加入10个令牌,最大令牌为5,因此只能加入5个$add_num=$tokenBucket->add(10);var_dump($add_num);//循环获取令牌,令牌桶内只有5个令牌,因此末了1次获取失落败for($i=0;$i<6;$i++){var_dump($tokenBucket->get());}?>领取办法:点赞关注

领取办法:点赞关注小编后私信【资料】获取资料领取办法!

标签:

相关文章

临武区,绿色发展的璀璨明珠

临武区,位于我国湖南省郴州市,是一个有着丰富自然资源和悠久历史的地方。近年来,临武区以绿色发展为引领,实现了经济社会与生态环境的协...

PHP教程 2024-12-23 阅读0 评论0

中止代码8,介绍网络安全的“无形守护者”

随着互联网的普及,网络安全问题日益凸显。无数黑客、病毒、恶意软件企图入侵我们的电脑,窃取个人信息,破坏网络秩序。在这场没有硝烟的战...

PHP教程 2024-12-23 阅读0 评论0

中美协议漏洞,漏洞背后的风险与应对步骤

近年来,中美关系在全球范围内备受关注。为了维护两国间的稳定与互利共赢,中美两国政府签署了一系列协议。这些协议中存在一些漏洞,给双方...

PHP教程 2024-12-23 阅读0 评论0

中华烟,传承与创新并进的民族品牌

中华烟,作为中国烟草行业的领军品牌,历经百年风雨,承载着中华民族的文化底蕴和民族精神。自诞生之日起,中华烟便以其独特的品牌魅力,赢...

PHP教程 2024-12-23 阅读0 评论0

东方餐,探寻中华美食的独特魅力

自古以来,中国饮食文化源远流长,被誉为“舌尖上的中国”。在这片广袤的土地上,各地美食争奇斗艳,其中东方餐作为中华美食的重要组成部分...

PHP教程 2024-12-23 阅读0 评论0