您现在的位置是:网站首页 > 心得笔记
PHP对API进行限流
简介在开发API时,为了防止恶意请求和保证系统的稳定性,我们经常需要对API进行限流。本文将介绍如何使用PHP对API进行限流。
为什么要限流
限流在很多场景中用来限制并发和请求量,比如说秒杀抢购,保护自身系统和下游系统不被巨型流量冲垮等
限流的思想
在保证可用的情况下尽可能多增加进入的人数,其余的人在排队等待,或者返回友好提示,保证里面的进行系统的用户可以正常使用,防止系统雪崩。
接口限流的常用算法
计数器法
计数器法也叫做 固定窗口算法。是限流算法里最简单也是最容易实现的一种算法。在一段时间间隔内(时间窗/时间区间),处理请求的最大数量固定,超过部分不做处理。
以下举例基于redis的计数器实现
假设客户端访问时,每个客服端都有一个IP,把IP作为redis存储的键,每分钟可以访问10次或者是20次,这样做就是在规定的时间内限制访问 次数,超过这个次数的就直接拒绝访问。 class RedisNumController { #1分钟的访问次数不能超过100个。 private $redis; private $maxNumber = 100; private $param; private $time = 60; public function __construct(Request $request) { $this->redis = RedisCon::getInstace(); $this->param = $request; } public function index() { $ip = $this->param->getClientIp(); #给当前的请求IP加一个过期时间 $this->redis->expire($ip,$this->time); #每次请求放入redis中自增 $num = $this->redis->incr($ip); #判断是否超过我们限制的最大次数 if ($num > $this->maxNumber) { exit('请求数据太过于频繁,请稍后再试!!!'); } echo '得到数据'.time(); } }
计数器算法很简单,但是有一个严重的bug:
一个恶意用户在0:59时瞬间发送了100个请求,然后再1:00时又瞬间发送了100个请求,那么这个用户在2秒内发送了200个请求。上面我们规定1分钟最多处理100个请求, 也就是每秒1.7个请求。用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过系统的承载能力,导致系统挂起或宕机。上面的问题,其实是因为我们统计的精度太低造成的。
2.令牌桶算法
实现原理:
系统以固定的速率向桶中添加令牌;
当有请求到来时,会尝试从桶中移除一个令牌;如果桶中有足够的令牌,则请求可以被处理;
如果桶中没有令牌,那么请求将被拒绝;
桶中的令牌数不能超过桶的容量,如果新生成的令牌超过了桶的容量,那么超出的令牌会被丢弃;
令牌桶算法的一个重要特性是,它能够应对突发流量。当桶中有足够的令牌时,可以一次性处理多个请求,这对于需要处理突发流量的应用场景非常有用。但是又不会无限制的增加处理速率导致压垮服务器,因为桶内令牌数量是有限制的。
代码实现
class TokenBucketLimiter { //桶的最大容量 public static $threshold = 10; //桶内当前的令牌数量 public static $count = 0; //令牌生成速率(每秒5次) public static $tokenRate = 5; //上次生成令牌的时间(毫秒) public static $lastRefillTime = round(microtime(true) * 1000); /** * 限流方法,返回true表示通过 */ public function limit() { // 调用生成令牌方法 $this->refillTokens(); // 判断桶内是否还有令牌 if ($count > 0) { $count--; return true; } return false; } /** * 生成令牌方法,计算并更新这段时间内生成的令牌数量 */ private function refillTokens() { $currentTime = round(microtime(true) * 1000); //计算这段时间内,需要生成的令牌数量 $refillTokens = ($currentTime - lastRefillTime) * $tokenRate / 1000; $count = min($count + $refillTokens, $threshold); $lastRefillTime = $currentTime; } }
优点:
可以处理突发流量:令牌桶算法可以处理突发流量。当桶满时,能够以最大速度处理请求。这对于需要处理突发流量的应用场景非常有用。 限制平均速率:在长期运行中,数据的传输率会被限制在预定义的平均速率(即生成令牌的速率)。 灵活性:与漏桶算法相比,令牌桶算法提供了更大的灵活性。例如,可以动态地调整生成令牌的速率。
缺点:
可能导致过载:如果令牌产生的速度过快,可能会导致大量的突发流量,这可能会使网络或服务过载。 需要存储空间:令牌桶需要一定的存储空间来保存令牌,可能会导致内存资源的浪费。 实现稍复杂:相比于计数器算法,令牌桶算法的实现稍微复杂一些。
上一篇:MySQL 如何优化慢查询?
下一篇:微服务架构中的熔断、限流与降级