为什么要做限流
首先让咱们先看一看零碎架构设计中,为什么要做“限流”。
旅游景点通常都会有最大的接待量,不可能无限度的放游客进入,比方故宫每天只卖八万张票,超过八万的游客,无奈买票进入,因为如果超过八万人,景点的工作人员可能就忙不过来,过于拥挤的景点也会影响游客的体验和情绪,并且还会有安全隐患;只卖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 次这样的参数,它是不适宜做这样的限流的,因为会耗费大量的存储空间。
前面还有漏桶算法和令牌桶算法,因为各自的实现比较复杂,所以筹备各自新开一篇文章独自形容