限流指的是限制系统的并发访问策略,保证系统能够接受部分用户的请求,而对于超过流量控制的请求,系统会拒绝请求。限流是为了保护系统不会被过载的流量打垮,其通常做在服务入口层如网关,它可以对整个系统,某个服务,某个接口,某个IP或者某个用户做限制。
常用的限流算法有固定窗口,滑动窗口,漏桶算法与令牌桶算法。
固定窗口
固定窗口指的是在固定的时间窗口内限制请求的数量,超过数量的请求直接拒绝。比如限制1分钟内只能接收1W笔请求,超过1W笔的请求直接拒绝请求,等到下一个一分钟重新开始计数。从固定窗口的算法中可以看出,在上面的例子中,如果请求全部集中在最后的1秒中,下一个一分钟的请求又全部集中在前1秒中,那么在时间窗口的交界处,QPS达到2W/S,从而导致临界区内超过了限流的阈值。
滑动窗口
由于固定窗口不能控制临界区内的请求数量,为此引入了滑动窗口,滑动窗口是将固定窗口范围继续划分出多个小窗口,比如上个例子中,将1分钟按1秒的间隔划分为60个小窗口,每个小窗口维护一个计数,滑动窗口将限制任意连续的60个小窗口的请求之和不超过1W。滑动窗口很好的解决了固定窗口算法在两个临界的窗口的请求过于集中的问题,但是还是不能平滑请求,因为一个时间窗口内的请求,可能全部聚集于某个小窗口内,同时还增加了算法的复杂度。
为此在实际项目中,我们很少基于时间窗口的限流算法,一般使用漏桶算法与令牌桶算法。
漏桶算法
漏桶算法就像是在流量的生产者与消费者之间增加一个漏桶,漏桶以每秒固定的速率流出流量,当漏桶满了后,生产者将不能在往漏桶中添加请求。漏桶算法一般通过一个有界的队列实现,当队列满了就不能往队列里面添加请求,从而实现将请求平滑的到达消费者。它能够很好的平滑请求,但是由于引入了队列缓存请求,增加了请求的响应时间,为此我们一般更推荐使用令牌桶算法。
令牌桶算法
令牌桶算法的基本思想是
1、如果我们要在1秒内限制N个请求,那么可以每隔1/N秒往桶里面放入一个令牌;
2、当有请求要处理的时候,就往桶里取一个令牌,同时减少令牌桶中令牌的数量;如果取不到令牌,则拒绝请求;
3、令牌桶的大小也需要有上限的,当令牌的个数超过N个,就不在往桶里面增加令牌。
令牌桶算法也可以很好的平滑请求,同时允许一定的突发流量。为此我们一般选择令牌桶算法来实现限流。
如果需要实现分布式限流我们一般使用redis来存储令牌,但是如果每次请求都从redis获取令牌,那么每个请求都会增加一次网络开销,为此在高并发的场景下,我们一般先预取一定数量的令牌。
限流阈值的设置
在实际的环境中我们一般很难确定一个准确的阈值,为此我们需要在配置中心中添加配置选项,以动态调整限流阈值。同时定期的压测需要限流的系统与微服务,然后修改配置中心中的阈值。