这是我参与更文挑战的第16天,活动详情查看:更文挑战
一、前言
服务限流:服务限流是限制请求的数量,即某个时间窗口内的请求速率。
二、限流算法
限流算法有:
- 计数器
- 滑动窗口计数
- 漏斗
- 令牌桶
(1)计数器
计数器:是使用计数器在周期内累加访问次数,当达到设定的阈值时,触发限流策略。下一个周期开始时,进行清零,重新计数。比如1分钟内限制请求总数为 100。如果超过100 则返回失败。
计数器的缺陷:
临界问题。 比如1分钟内限制请求总数为100的场景下,前一个一分钟内直到这一分钟快结束的时候才来了 100 个请求,而后一个一分钟刚开始就立即来了100个请求。 虽然是这两个不同的一分钟区间,但是事实上不到一分钟的时间内,来了200个请求。因此计数器限流失败。
如图:
简单实现如下:
class Counter {
public long timeStamp = System.currentTimeMillis();
public AtomicInteger reqCount = new AtomicInteger(0);
// 时间窗口内最大请求数
public final int limit = 5;
// 时间窗口ms 60s, 1分钟
public final long interval = 60 * 1000;
public boolean acquire() {
long now = System.currentTimeMillis();
if (now < timeStamp + interval) {
// 在时间窗口内
int cnt = reqCount.getAndIncrement();
// 判断当前时间窗口内是否超过最大请求控制数
return cnt <= limit;
} else {
// 重置
timeStamp = now;
// 超时后重置
reqCount.set(1);
return true;
}
}
}
(2)滑动窗口计数
滑动窗口计数:将时间周期进一步划分为 N 个小周期,分别记录每个小周期内访问次数,并且根据时间滑动删除过期的小周期。
如图:
滑动窗口的单位区间划分越多,滑动窗口的滚动就越平滑,限流统计就会越精确。
(3)漏斗
漏斗:是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被调整成为一个稳定的流量。
如图:
(4)令牌桶
令牌桶:某种程度上对漏斗算法的改进。
令牌桶能够在限制请求平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。 该算法以一定的速率往桶中添加令牌。每次请求需要先获取到桶中的令牌才能继续执行,否则等待可用的令牌,或者直接拒绝。
如图:
pom.xml
依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
- 测试
@Test
public void test() {
//创建一个每秒产生一个令牌的令牌桶
RateLimiter rateLimiter = RateLimiter.create(1);
for(int i = 1; i <= 5; i++) {
//一次获取i个令牌
double waitTime = rateLimiter.acquire(i);
System.out.println("acquire:" + i + " waitTime:" + waitTime);
}
}