滑动窗口算法弥补了计数器算法的不足。滑动窗口算法把间隔时间划分成更小的粒度,当更小粒度的时间间隔过去后,把过去的间隔请求数减掉,再补充一个空的时间间隔。
如下图所示,把1分钟划分为10个更小的时间间隔,每6s为一个间隔。
1 一个时间窗口为1分钟,滑动窗口分成10个格子,每个格子6秒。
2 每过6秒,滑动窗口向右移动1个格子。
3 每个格子都有独立的计数器。
4 如果时间窗口内所有的计数器之和超过了限流阀值,则触发限流操作。
如下图所示,滑动窗口算法比计数器算法控制得更精细。
用户在0:59 时刻发送了100个请求,第10个格子的计数器增加100,下一秒的时候时间窗口向右移动1格,这时再来100个请求就超过了阈值,不会处理这100个请求,这样就避免了计数器场景出现的问题。
滑动窗口设置得越精细,限流的效果越好,但滑动窗口的时间间隔(小格子)多了,存储的空间也会增加。
1 设计一个滑动窗口,窗口有10个格子,每个格子10秒,每隔10秒移动一格。
2 装满所有格子的时间为 10 * 10 = 100 秒。也就是说时间窗口是 100 秒。
3 从100秒开始,开始滑动,新请求数开始覆盖老请求数。
package currentLimit;
import java.util.Date;
import java.util.Random;
/**
* @className: CounterLimit
* @description: 滑动窗口算法
* @date: 2022/1/8
* @author: cakin
*/
public class SlideWindowLimit {
// 滑动窗口大小
static final int size = 10;
// 滑动窗口数组,每移动一个格子,更新对应数据项的值
static int window[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// 理解为移动窗口中正在计数的格子
static int curId = 0;
// 记录上次统计时间
static Date lastDate = new Date();
// 当前窗口计数总和
static int counter = 0;
/**
* 功能描述:模拟一次请求是否限流
*
* @return true:限流 false;不限流
* @author 贝医
* @date 2022/1/8
* @description:
*/
static boolean slideWindowLimit() {
// 获取当前时间
Date now = new Date();
// 当前时间同上次记录时间的间隔,单位为秒
long time = (now.getTime() - lastDate.getTime()) / 1000;
// 按照新的移动窗口进行计数
if (time >= 10) {
// 当前计数格子的下一个格子将被清掉重写
curId++;
curId = curId % size;
int newCurId = curId;
// 下一个格子将被清掉,总数据减掉
counter = counter - window[newCurId];
// 新格子设置为1
window[newCurId] = 1;
// 记录滑动的时间
lastDate = now;
} else {
// 当前计数的格子
++window[curId];
}
++counter;
return counter >= 1000;
}
// 测试方法
public static void main(String[] args) {
for (; ; ) {
// 模拟一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Random random = new Random();
int i = random.nextInt(3);
// 模拟1秒内请求8次
if (i == 1) {
for (int j = 0; j < 8; j++) {
if (slideWindowLimit()) {
System.out.println("限流了" + counter);
} else {
System.out.println("没限流" + counter);
}
}
} else if (i == 2) { // 模拟1秒内请求9次
for (int j = 0; j < 9; j++) {
if (slideWindowLimit()) {
System.out.println("限流了" + counter);
} else {
System.out.println("没限流" + counter);
}
}
} else { // 模拟1秒内请求10次
for (int j = 0; j < 10; j++) {
if (slideWindowLimit()) {
System.out.println("限流了" + counter);
} else {
System.out.println("没限流" + counter);
}
}
}
}
}
}
记录滑动窗口中的请求数。滑动窗口中的请求数控制在 1000以内。滑动窗口能记录100秒的请求,所以如果每秒请求不超过10,不会限流。测试用例也是这样设计的,每秒模拟发送的请求为8次,9次,10次。从测试结果来看,符合预期。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/chengqiuming/article/details/122392427
内容来源于网络,如有侵权,请联系作者删除!