赞
踩
关于限流算法有许多种,有简单的计数限流阀,固定窗口限流法,滑动窗口限流法,漏桶算法,令牌桶算法。今天我们就来聊聊滑动窗口版的限流算法。
说起滑动窗口之前,我们先来说说固定窗口限流。固定窗口限流,我们可以这样子来理解,我们假设规定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); } }
在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); } } }
通过以上的了解了固定窗口版的限流算法,是不是看上去没什么毛病?其实不然,我们想一想,如果一秒钟允许500个请求,如果这500个请求都在这1s钟的最后100ms过来那么是可以通过的,然后下一秒的前100ms又过来了500个请求,那么这个算法也是允许通过的,那么问题就来了,再这短短的200ms就已经过去了1000个请求,这也是这个算法的一个缺陷所在,面对突增的流量以及刚好又在上一秒和下一秒的切换的时候,可能没有起到很好的防护
所以下面就引出我们的滑动窗口版的限流算法
滑动窗口版的算法我们可以这么来理解
我们可以把1s 以 200ms 分割成了5个小窗口,然后每个小窗口会记录这段时间内的请求数量,如果总和已经超过了最大请求量那么就拒绝请求,然后每隔200ms 我们往右边滑动,顺便减去左边被淘汰出去的窗口的请求数,我们可以用下图来理解(1s三个请求)
每个大窗口会记载当前这1s内有几个请求了,然后每隔200ms往左滑动,然后减去最左边被淘汰的小窗口的请求数量,可以发现,如果小窗口的越小那么越平滑,如果小窗口的大小也是1s,那么就退化成固定窗口了。。如果使用滑动窗口的话,小窗口大小为200ms,那么就不会发生上面固定窗口的那种情况了
下一篇文章我们再来实现这样子的一个限流算法
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。