赞
踩
滑动窗口算法弥补了计数器算法的不足。滑动窗口算法把间隔时间划分成更小的粒度,当更小粒度的时间间隔过去后,把过去的间隔请求数减掉,再补充一个空的时间间隔。
如下图所示,把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次。从测试结果来看,符合预期。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。