在开发接口服务器的过程当中,为了防止客户端对于接口的滥用,保护服务器的资源, 一般来讲咱们会对于服务器上的各类接口进行调用次数的限制。好比对于某个 用户,他在一个时间段(interval)内,好比 1 分钟,调用服务器接口的次数不可以 大于一个上限(limit),好比说 100 次。若是用户调用接口的次数超过上限的话,就直接拒绝用户的请求,返回错误信息。
服务接口的流量控制策略:分流、降级、限流等。本文讨论下限流策略,虽然下降了服务接口的访问频率和并发量,却换取服务接口和业务应用系统的高可用。php
一、漏桶算法
漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以必定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),而后就拒绝请求,能够看出漏桶算法能强行限制数据的传输速率.示意图以下:
redis
可见这里有两个变量,一个是桶的大小,支持流量突发增多时能够存多少的水(burst),另外一个是水桶漏洞的大小(rate)。
由于漏桶的漏出速率是固定的参数,因此,即便网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流突发(burst)到端口速率.所以,漏桶算法对于存在突发特性的流量来讲缺少效率.算法
二、令牌桶算法
令牌桶算法(Token Bucket)和 Leaky Bucket 效果同样但方向相反的算法,更加容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(若是QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),若是桶已经满了就再也不加了.新请求来临时,会各自拿走一个Token,若是没有Token可拿了就阻塞或者拒绝服务.数据库
令牌桶的另一个好处是能够方便的改变速度. 一旦须要提升速率,则按需提升放入桶中的令牌的速率. 通常会定时(好比100毫秒)往桶中增长必定数量的令牌, 有些变种算法则实时的计算应该增长的令牌的数量.json
<?php namespace Api\Lib; /** * 限流控制 */ class RateLimit { private $minNum = 60; //单个用户每分访问数 private $dayNum = 10000; //单个用户天天总的访问量 public function minLimit($uid) { $minNumKey = $uid . '_minNum'; $dayNumKey = $uid . '_dayNum'; $resMin = $this->getRedis($minNumKey, $this->minNum, 60); $resDay = $this->getRedis($minNumKey, $this->minNum, 86400); if (!$resMin['status'] || !$resDay['status']) { exit($resMin['msg'] . $resDay['msg']); } } public function getRedis($key, $initNum, $expire) { $nowtime = time(); $result = ['status' => true, 'msg' => '']; $redisObj = $this->di->get('redis'); $redis->watch($key); $limitVal = $redis->get($key); if ($limitVal) { $limitVal = json_decode($limitVal, true); $newNum = min($initNum, ($limitVal['num'] - 1) + (($initNum / $expire) * ($nowtime - $limitVal['time']))); if ($newNum > 0) { $redisVal = json_encode(['num' => $newNum, 'time' => time()]); } else { return ['status' => false