滑动时间窗算法通常被用于系统限流,限流的目的是通过对并发访问/请求进行限速,或者对一个时间段内的请求进行限速来保护系统,一旦达到限制速率程序可以进行拒绝、等待或降级等操作。
滑动时间窗算法是针对 固定窗口算法 的改良,一定程度上缓解了固定窗口算法下的临界问题,它将单位时间周期分为n个小周期,分别记录每个小周期内接口的访问次数,并且根据时间滑动删除过期的小周期。
当滑动时间窗的格子越小,那么窗口的滚动就越平滑,限流就会越精确。
Java实现
实场景中通常使用Redis的有序集合(sorted set)来实现
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
| public class SlideWindowLimiter { private final long windowTime; private final int limitNum; private final List<Long> windows;
public SlideWindowLimiter(long windowTime, int limitNum) { this.windowTime = windowTime; this.limitNum = limitNum; this.windows = new LinkedList<>(); }
public synchronized boolean outOfLimit() { long now = System.currentTimeMillis(); if (this.windows.size() < this.limitNum) { this.windows.add(0, now); return true; }
int lastIndex = this.limitNum - 1; Long lastTime = this.windows.get(lastIndex); if (this.windowTime >= now - lastTime) { return false; } else { this.windows.remove(lastIndex); this.windows.add(0, now); return true; } }
}
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static void main(String[] args) throws Exception { SlideWindowLimiter limiter = new SlideWindowLimiter(2 * 1000L, 3); System.out.println(limiter.outOfLimit()); System.out.println(limiter.outOfLimit()); Thread.sleep(990L); System.out.println(limiter.outOfLimit()); System.out.println(limiter.outOfLimit()); System.out.println(limiter.outOfLimit()); Thread.sleep(990L); System.out.println(limiter.outOfLimit()); System.out.println(limiter.outOfLimit()); Thread.sleep(990L); System.out.println(limiter.outOfLimit()); Thread.sleep(990L); System.out.println(limiter.outOfLimit()); System.out.println(limiter.outOfLimit()); System.out.println(limiter.outOfLimit()); }
|
结果
1 2 3 4 5 6 7 8 9 10 11
| true true true false false false false true true true false
|
缺点
滑动时间窗算法虽然解决了 固定窗口算法 的临界问题,但是程序一旦到达限流次数后,程序的操作会被拒绝,会丢失相应的数据,所以这种算法一般用于对外的openAPI中,对于系统内部的业务实现,其实并不友好。