为什么要做限流

首先让咱们先看一看零碎架构设计中,为什么要做“限流”。

旅游景点通常都会有最大的接待量,不可能无限度的放游客进入,比方故宫每天只卖八万张票,超过八万的游客,无奈买票进入,因为如果超过八万人,景点的工作人员可能就忙不过来,过于拥挤的景点也会影响游客的体验和情绪,并且还会有安全隐患;只卖N张票,这就是一种限流的伎俩

软件架构中的服务限流也是相似,也是当系统资源不够的时候,曾经不足以应答大量的申请,为了保障服务还可能失常运行,那么依照规定,零碎会把多余的申请间接回绝掉,以达到限流的成果

不晓得大家留神过没有,比方双11,刚过12点有些顾客的网页或APP会显示下单失败的提醒,有些就是被限流掉了。

常见的限流算法

计数法

顾名思义就是来一个,记录一个,比方我1分钟只能解决1000个申请,那么咱们就能够设置一个计数器,来一个申请就incr+1,当1分钟之内的数量大于等于1000之后不解决了即可,伪代码如下

$redis = new Redis();$redis->connect('127.0.0.1', 6379);$rate_limit = 1000;  //限度个数$rate_seconds = 60;  //限度工夫$redis_key = "redis_limit";$count = $redis->get($redis_key);if ($count >= $rate_limit){  //判断60秒内申请个数是否曾经达到下限    //间接返回,不解决申请    return}$redis->incr($redis_key, 1);//申请计数$redis->expire($redis, $rate_seconds); //设置过期工夫 60s//to do  业务逻辑解决.......

这种计数形式比较简单快捷,然而有很大的毛病,因为申请的拜访不肯定是很安稳的,如果0:59过去了1000个申请,1:01曾经是下一个窗口,又过去了1000个申请,但实际上三秒内来了2000个申请,曾经超过咱们的限流下限了。所以这种办法是不举荐的。

滑动窗口算法

还拿下面的例子,一分钟分6份,每份10秒;每过10秒钟,咱们的工夫窗口就会往右滑动一格,每个格子都有独立的计数器,咱们每次都计算工夫窗口内的数量,能够解决计数器法中的问题,而且当滑动窗口的格子越多,那么限流的统计就会越准确。具体能够参考下图,看图比拟清晰

伪代码实现如下

function api_limit($scene,  $period, $maxCount){    $redis = new Redis();    $redis->connect('127.0.0.1', 6379);    $key = sprintf('hist:%s', $scene); //限流场景惟一标识    $now = msectime();   // 毫秒工夫戳,这样更准确    $pipe=$redis->multi(Redis::PIPELINE); //应用管道晋升性能    $pipe->zadd($key, $now, $now); //value 和 score 都应用毫秒工夫戳    $pipe->zremrangebyscore($key, 0, $now - $period); //移除工夫窗口之前的行为记录,剩下的都是工夫窗口内的    $pipe->zcard($key);  //获取窗口内的行为数量    $pipe->expire($key, $period/1000 + 1);  //多加一秒过期工夫    $replies = $pipe->exec();    return $replies[2] <= $maxCount;  //$replies[2]为zcard返回的个数  如果zcard后果大于maxCount,则不处理结果}for ($i=0; $i<20; $i++){  //测试限流是否实现代码    var_dump(isActionAllowed("uniq_scene", 60*1000, 5)); //执行能够发现只有前5次是通过的}//返回以后的毫秒工夫戳function msectime() {    list($msec, $sec) = explode(' ', microtime());    $msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);    return $msectime; }

这段代码还是略显简单,须要读者花肯定的工夫好好啃。它的整体思路就是:每一个行为到来时,都保护一次工夫窗口。将工夫窗口外的记录全副清理掉,只保留窗口内的记录。

因为这几个间断的 Redis 操作都是针对同一个 key 的,应用 pipeline 能够显著晋升Redis 存取效率。但这种计划也有毛病,因为它要记录时间窗口内所有的行为记录,如果这个量很大,比方限定 60s 内操作不得超过 100w 次这样的参数,它是不适宜做这样的限流的,因为会耗费大量的存储空间

前面还有漏桶算法和令牌桶算法,因为各自的实现比较复杂,所以筹备各自新开一篇文章独自形容