本文實現了一種基於java的滑動時間窗口計數器算法
滑動時間窗口計數器算法思想:針對固定時間算法會在臨界點存在瞬間大流量衝擊的場景,滑動時間窗口計數器算法應運而生。它將時間窗口劃分爲更小的時間片段,每過一個時間片段,我們的時間窗口就會往右滑動一格,每個時間片段都有獨立的計數器。我們在計算整個時間窗口內的請求總數時會累加所有的時間片段內的計數器。時間窗口劃分的越細,那麼滑動窗口的滾動就越平滑,限流的統計就會越精確。
本文代碼邏輯:新建一個本地緩存,每5s爲一個時間窗口,每1s爲一個時間片段,時間片段作爲緩存的key,原子類計數器作爲緩存的value。每秒發送隨機數量的請求,計算每個時間片段的前5秒內的累加請求數量,超出閾值則限流。
@Slf4j
public class WindowLimiter {
private LoadingCache<Long, AtomicLong> counter =
CacheBuilder.newBuilder()
.expireAfterWrite(10, TimeUnit.SECONDS)
.build(new CacheLoader<Long, AtomicLong>() {
@Override
public AtomicLong load(Long seconds) throws Exception {
return new AtomicLong(0);
}
});
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
//限流閾值
private long limit = 15;
/**
* 滑動時間窗口
* 每隔1s累加前5s內每1s的請求數量,判斷是否超出限流閾值
*/
public void slideWindow() {
scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
long time = System.currentTimeMillis() / 1000;
//每秒發送隨機數量的請求
int reqs = (int) (Math.random() * 5) + 1;
counter.get(time).addAndGet(reqs);
long nums = 0;
// time windows 5 s
for (int i = 0; i < 5; i++) {
nums += counter.get(time - i).get();
}
log.info("time=" + time + ",nums=" + nums);
if (nums > limit) {
log.info("限流了,nums=" + nums);
}
} catch (Exception e) {
log.error("slideWindow error", e);
} finally {
}
}, 5000, 1000, TimeUnit.MILLISECONDS);
}
}
輸出結果:
20:14:50.083 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079290,nums=15
20:14:51.114 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079291,nums=13
20:14:52.119 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079292,nums=14
20:14:53.121 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079293,nums=15
20:14:54.126 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079294,nums=17
20:14:54.127 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - 限流了,nums=17
20:14:55.128 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079295,nums=14
20:14:56.131 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - time=1574079296,nums=17
20:14:56.132 [pool-1-thread-3] INFO com.example.demo.limit.WindowLimiter - 限流了,nums=17