言午月月鸟
编程,带娃以及思考人生
首页
编程
带娃
思考人生
编程画图秀
PHP系统解析-频率控制
dingusxp
2911
# PHP系统解析-频率控制 ## 是什么? > 频率控制(或叫接口限流),是指通过控制服务请求的**准入QPS**,让系统处于一个相对安全的负载空间,提升系统稳定性。 ## 限流场景 包括但不限于: - 限制某接口的访问;(**常用**) - 限制用户的总访问/用户对某接口的访问;(用户可能是业务用户ID,也可能是前端生成的uuid) - 限制IP的总访问/IP对某接口的访问。(慎用) ## 常用算法 - 计数器法 计算器法思路简单,即定义一定时间范围内可处理的请求量,直接计数判断。 **实现参考:** 假设限定接口 A的频率为 t秒钟总访问不超过 n。 每个请求过来,在redis 当前计数key = limit_A_intval(time()/t) 上执行 incr 操作,如果返回值 >n,则表示触发阈值,即 命中超限。 变种有 **滑动窗口法**,思路为: 不是严格按照 t 周期进行计数刷新,而是将一个周期分为若干段(窗口)进行计数,再加和统计。如周期为1分钟,分为6段,每10s一段计数。统计判断时,取当前往前共6段加和,再比较阈值。 - 漏桶算法 漏桶算法的思路是:定义一个桶的容量(即单位时间接口能hold住的请求数),以及漏水速度(即接口正常的处理速率)。当请求进来,相当于往桶里注水。当水漏出速度小于流入速度,水会在桶里挤压,当桶注满之后,新的请求过来将触发超限。 **实现参考:** 假设限定接口A的桶容量为n,漏水速率为q/每秒。 请求进来,先去抢锁 set('update_lock_A', true, ['nx', 'exp' => 1]),如果抢到,则进入漏水操作; 漏水操作:取上次漏水时间操作 get('update_ts_A'),根据当前时间与上次操作时间差,计算需要漏水的量 m=q*时间差,进行漏水操作 decr('container_A', m)。注意保底,如果decr返回负值,重置为 0。 没抢到锁或者漏水操作完成后,进行注水操作,incr('container_A'),如果返回计数 >n,则**扣除该次入水**并返回命中超限。 - 令牌桶算法 令牌桶算法的思路是:设定一个令牌发放器,定期为每个接口发放一定量令牌,令牌存入桶中(有上限),执行请求会消耗桶中的令牌。 **实现参考:** 假设限定接口A的处理能力为q(/每秒),令牌桶的上限为 n。 请求进来,先去抢锁 set('update_lock_A', true, ['nx', 'exp' => 1]),如果抢到,则进入取令牌操作; 取令牌操作:取上次操作时间 get('update_ts_A'), 根据当前时间与上次操作时间差,计算要补充的令牌数 m=q*时间差,灌入令牌 incr('container_A', m)。如果令牌数超过上限 n,重置为 n。 没抢到锁或者取令牌操作完成,进行扣减令牌操作 decr('container_A'),如果返回计数 ≤ 0,则恢复为 0并返回命中超限。 可以看到令牌桶算法与漏桶算法非常相似,只不过一个是先扣后加,一个是先加后扣。但通常使用令牌桶算法更多,主要是:1)思路更好理解;2)如果需要临时干预,可以通过旁路程序直接往桶里加/减令牌来控制。 实现上与 redis 交互次数较多,每个请求至少2次,这是PHP语言(CGI模式)实现上的妥协。如果是其它语言或者 PHP swoole扩展下,可以实现得更加高效。 大家可以自己实现一下上面的几种算法,并模拟几种突发流量,看看 实际通过的请求量、被拒的请求量 与 时间的曲线。 ## 解决方案 - Laravel 的 throttle 中间件 - hyperf 的 [令牌桶限流器](https://hyperf.wiki/2.0/#/zh-cn/rate-limit) ## 参考文章与拓展阅读 [令牌桶限流思路分享(PHP+Redis实现机制)](https://www.jianshu.com/p/9f76dd2757c7) [php如何对api接口限流](https://www.php.cn/php-ask-431047.html) [高并发之API接口限流](https://www.cnblogs.com/daofaziran/p/10506197.html)
粤ICP备19051469号-1
Copyright©dingusxp.com - All Rights Reserved
Template by
OS Templates