当前位置:   article > 正文

限流算法之----滑动窗口_滑动窗口限流算法

滑动窗口限流算法

关于限流算法有许多种,有简单的计数限流阀,固定窗口限流法,滑动窗口限流法,漏桶算法,令牌桶算法。今天我们就来聊聊滑动窗口版的限流算法。
说起滑动窗口之前,我们先来说说固定窗口限流。固定窗口限流,我们可以这样子来理解,我们假设规定1s可以放入3个请求,那么窗口的大小就是1s,然后在1s内对请求计数,如果超过3那么就限流,过了这一秒后计数清空。
可以用下图来理解
在这里插入图片描述

这边放一下我写的固定窗口的限流算法线程安全版给大家参考一下
主要是利用juc包下的原子类和cas的操作以及一些状态的判断

/**
 * @Author: chencc
 */
public class FixedWindowRateLimit {
    /**
     * 窗口开始的时间 -1代表还没被初始化
     */
    AtomicLong startTime = new AtomicLong(-1);
    /**
     * 计数
     */
    AtomicInteger count = new AtomicInteger(0);
    /**
     * 最大请求数
     */
    private final int MAX_REQUEST_NUM = 100;

    /**
     * 判断当前是否能够继续往下进行
     * @return
     */
    public boolean entry() {
        long time = System.currentTimeMillis();
        for (;;) {
            // 如果第一次没有初始化
            if (startTime.get() == -1) {
                if (lockStartTime(-1)) {
                    count.set(1);
                    unlockStartTime(time);
                    return true;
                }
            }
            // 如果是-2 证明窗口处于锁定状态 让出cpu
            if (startTime.get() == -2) {
                Thread.yield();
                continue;
            }
            // 如果这个请求的时间已经小于当前这个窗口的时间 更新时间
            if (time < startTime.get()) {
                time = System.currentTimeMillis();
            }
            // 判断当前时间和窗口已经相差了一秒
            long expect = startTime.get();
            if (time - expect >= 1000) {
                if (lockStartTime(expect)) {
                    count.set(1);
                    unlockStartTime(time);
                    return true;
                }
            }
            // 如果已经超过的最大请求数
            if (count.get() >= MAX_REQUEST_NUM && startTime.get() > 0) {
                return false;
            }
            int num;
            for (;(num = count.get()) < MAX_REQUEST_NUM && startTime.get() > 0 && time >= startTime.get();) {
                if (count.compareAndSet(num, num + 1)) {
                    return true;
                }
            }
        }
    }

    /**
     * -2代表当前被另外一个线程再使用
     * @param expect
     * @return
     */
    private boolean lockStartTime(long expect) {
        return startTime.compareAndSet(expect, -2);
    }

    /**
     * 解除锁定
     * @param time
     */
    private void unlockStartTime(long time) {
        startTime.set(time);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

spingboot的环境下可以通过拦截器这样子来测试

/**
 * @author chencc
 * @date 2022/8/2 21:42
 */
@Configuration
@Component
public class RateLimitFilter implements Filter {

    @Autowired
    private FixedWindowRateLimit fixedWindowRateLimit;

    @Bean
    public FixedWindowRateLimit getFixedWindowRateLimit() {
        return new FixedWindowRateLimit();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (!fixedWindowRateLimit.entry()) {
            response.getWriter().write("rate limit");
        } else {
            chain.doFilter(request, response);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

通过以上的了解了固定窗口版的限流算法,是不是看上去没什么毛病?其实不然,我们想一想,如果一秒钟允许500个请求,如果这500个请求都在这1s钟的最后100ms过来那么是可以通过的,然后下一秒的前100ms又过来了500个请求,那么这个算法也是允许通过的,那么问题就来了,再这短短的200ms就已经过去了1000个请求,这也是这个算法的一个缺陷所在,面对突增的流量以及刚好又在上一秒和下一秒的切换的时候,可能没有起到很好的防护
所以下面就引出我们的滑动窗口版的限流算法
滑动窗口版的算法我们可以这么来理解
我们可以把1s 以 200ms 分割成了5个小窗口,然后每个小窗口会记录这段时间内的请求数量,如果总和已经超过了最大请求量那么就拒绝请求,然后每隔200ms 我们往右边滑动,顺便减去左边被淘汰出去的窗口的请求数,我们可以用下图来理解(1s三个请求)
在这里插入图片描述
每个大窗口会记载当前这1s内有几个请求了,然后每隔200ms往左滑动,然后减去最左边被淘汰的小窗口的请求数量,可以发现,如果小窗口的越小那么越平滑,如果小窗口的大小也是1s,那么就退化成固定窗口了。。如果使用滑动窗口的话,小窗口大小为200ms,那么就不会发生上面固定窗口的那种情况了
下一篇文章我们再来实现这样子的一个限流算法

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/274674
推荐阅读
相关标签
  

闽ICP备14008679号