在Java的系统中,在一些活动日或者是被,导致访问量突然暴增,系统承受不了巨大的流量冲击而崩溃。为了保护我们的系统,在实际开发中有四种常见的限流算法来保证系统的安全性。

1 固定窗口算法

固定窗口算法的原理很简单。就是在时间轴上,限制单位时间内的请求数量。

实现步骤如下:

1. 使用一个计数器记录单位时间内的访问数量

2. 当访问数量没有达到阀值,就允许访问;当访问数量达到阀值,就拒绝访问

3. 当时间窗口过去后,计数器重新计数。

api java 数据流量统计 java实现流量控制_spring


实现很简单,但也存在问题。假设时间窗口大小为1秒,窗口内限制的请求个数为10, 如果再 0.5-1.0秒来10个请求,在1.0-1.5秒又来10个请求,此时0.5到1.5中间间隔一个时间窗口,实际上已经通过了20个请求。超过了原有的时间窗口内请求个数的限制两倍。

2 滑动窗口算法

滑动窗口算法是固定窗口算法的优化版本,主要是为了解决固定窗口中的零界值问题导致限流失败的问题。优化的地方如下:

将一个时间窗口分为5份。每一份里面都有一个独立计数器c。在时间轴上的一个时间窗口内,没当请求过来的时候,就会求计数器 c1+c2+c3+c4+c5的和,当达到阀值就拒绝,没达到当前小格子里面的计数器就加1 。

过程如下:

api java 数据流量统计 java实现流量控制_滑动窗口_02


当时间窗口滑动一小格后如下:

api java 数据流量统计 java实现流量控制_spring_03


这是会重新计算这个窗口内所有时间小格子中的计数记录的请求数之和,当达到阀值之后,就不拒绝访问。因此当1.0到1.2这个格子中出现10个请求时,只能放1个请求进来,拒绝9个。

这样可以有效避免固定窗口算法中的问题。当格子划分的越小,这个滑动窗口就越平滑。请求就越均匀。但是达到阀值就拒绝,这个在实际业务场景中过于暴力。因此这个算法也不是最理想的解决方案。

3 漏桶算法

漏桶算法关键在,当有大量请求过来时,用一个类似桶的容器给装起来,然后匀速放水。这样就可以有效起到削峰填谷的作用。保证服务的稳定运行。如下图:

api java 数据流量统计 java实现流量控制_java_04

漏桶算法虽然能保证我们的应用能匀速消费请求。当时也存在一个问题就是桶容量较大,且桶装满之后,会拒绝新的请求,桶中的请求消费会有延迟。

4 令牌桶算法

令牌桶算法是漏桶算法的改进版本。原理是在以匀速往桶里面放令牌,桶满了就暂停发放令牌。每当请求过来时,需要从桶中去获取一个令牌,只有获取到令牌请求才处理(这样可以有效避免漏桶算法中请求处理延时的问题),获取不到就拒绝服务。

实现原理如下(网上扣的图,比较能表达令牌桶算法的思想):

api java 数据流量统计 java实现流量控制_滑动窗口_05

现在多数场景下的限流都是基于令牌桶的思想去做的

4.1 单机限流

在限制单台服务器并发流量时,google的guava库中给了很优秀的实现。

4.2 分布式限流

微服务架构中对整个集群的常见限流方案在Spring Cloud Circuit Breaker组件文档中都有说明,可以去翻阅。