高并发之限流

在应对高并发场景的时候,我们会采用多种方式来保证系统的高可用,尽量保证用户体验。而其中我们最常用的三把刀,就是限流、降级、缓存等。

缓存,我们常用的可以用如redis之类的分布式缓存进行处理。降级,可以从服务降级、服务熔断的方式去做。而这篇文章,我们主要讨论一下关于限流方面的事情。


限流,字面上解释就是当系统的访问增大,如QPS增大,此时我们需要对流量进行流量控制。这里我们讲讲几种常用的限流算法。

1.计数器

计数器方法的一个典型的应用场景就是规定在固定的时间内,只允许通过固定数量的请求,如规定在一分钟内只允许处理100个请求。计数器的基本思想是:有一个counter。当一个请求到来的时候,首先判断现在是否是处于新的时间段里。如果是在新的时间段了,就可以把counter清0,然后对当前这个请求进行计数加一操作。假如没有处于新的时间段,则判断当前的counter的数量是否超过规定的数量,如100。超过了就丢弃请求,否则对计数器加一,允许该请求通过。

示例代码(代码源自http://www.cnblogs.com/clds/p/5850070.html

public class CounterDemo {
public long timeStamp = getNowTime();
public int reqCount = 0;
public final int limit = 100; // 时间窗口内最大请求数
public final long interval = 1000; // 时间窗口ms
public boolean grant() {
long now = getNowTime();
if (now < timeStamp + interval) {
// 在时间窗口内
reqCount++;
// 判断当前时间窗口内是否超过最大请求控制数
return reqCount <= limit;
}
else {
timeStamp = now;
// 超时后重置
reqCount = 1;
return true;
}
}
}

计数器的最大的一个问题就在于精度不够。当在时间的临界区的时候,可能会出现问题。如我规定1分钟内只能处理100,因为我的系统设计是1分钟内只能处理100个请求。假如我在59秒的时候,发起了100个请求。在刚好1分钟,进入下一阶段的时候,我又发起100个请求(因为现在已经进入新的阶段,计数器刚刚清空)。那么也就是说我在两秒钟内一共发了200个请求(0:59一次,1:00一次),超过了系统设计:一分钟内只能处理100个。因此可能会出问题。

那么如何解决呢?进一步的可以采用滑动窗口算法

2.滑动窗口算法

滑动窗口是我们比较熟悉的算法了。没错,就是和TCP里面的流量控制的滑动窗口一个意思。只不过有点小区别。

假如我们现在还是要求1分钟内只能处理100,因为我的系统设计是1分钟内只能处理100个请求。如果用滑动窗口的话,我们可以将滑动窗口的分为6格,每格10s。每个格子都有自己的计数器。

基本的步骤是:当一个请求到来时,首先判断整个滑动窗口现在6个计数器之和是否大于100,如果大于,则不处理,如果小于,则对当前时间对应的那个小格子的计数器加一。每隔10s,都把窗口向右移动一格。

滑动窗口能有效防止上一个简单的计数器方法存在的问题。但是滑动窗口因为会有多个计数器,因此在内存使用上会多于计数器方法。

3.漏筒算法

漏筒算法,可以看看下面的漏斗图片
这里写图片描述

假如从漏斗上面倒水进去,那么在漏斗下发的流出速度是固定限定了的。在漏斗上面就没有明确限定。假如在漏斗上面已经溢出来了,那么就直接丢掉溢出的。所以采用漏斗方法可以用来限定流量的流出速度。

4.令牌筒算法

令牌筒方法的主要设计目的是在可以控制流量流入速度的同时,也能具有一定的弹性,能够应对短时间的多量请求,不至于流量一上去就超阈值然后丢消息。那么是怎么做的呢?
可以想象,假如我们现在是国庆放假(离国庆还有半个月,hhhhh),故宫要做限流措施,对故宫的游客数量进行控制。因此工作人员准备半个小时,派出100张票,也就是每半个小时生成100张票。假如游人很多,那么每次100张票都能在半小时内用完,其他游客没票了,只能在外面等着,不让进。那假如游人很少,半个小时100张没用完,那么就会累积在那儿,半个小时后又新生成100张。因此售票处的票会越来越累积起来。假如突然来了一个大型旅行团,游客数量很多,那么此时由于前面还累积了很多票,因此即使这个旅行团超过100,也能给他们每个人都发票。

上述描述就是令牌筒的基本流程。当请求到来时,需要进行令牌申请。申请到令牌才能进行后续处理。由于令牌生成后是可以累积的,在超过筒的大小之前,都是可以保留下来的(超过筒的容量就丢弃)。因此可以应对短时的大流量请求。

令牌筒的方法,在具体的使用中比较多。但是各个方法也各有优劣,因此需要对应具体的场景去使用

猜你喜欢

转载自blog.csdn.net/Snail_Ren/article/details/77990447