1、计数器算法
计数器算法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。那么我们可以这么做:在一开 始的时候,我们可以设置一个计数器counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100并且该请求与第一个 请求的间隔时间还在1分钟之内,那么说明请求数过多;如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置 counter,具体算法的示意图如下:
package com.test.限流算法.v1;
/**
* @Auther: sai
* @Date: 2022/7/13 0013 21:16
* @ClassName: CounterTest
* @Version: 1.0
* @Description:
*/
public class CounterTest {
public long timeStamp = getNowTime();
public int reqCount = 0;
public final int limit = 5; // 时间窗口内最大请求数
public final long interval = 1000; // 时间窗口ms
public boolean grant() {
long now = getNowTime();
if (now < timeStamp + interval) {
// 在时间窗口内
reqCount++;
// 判断当前时间窗口内是否超过最大请求控制数
return reqCount <= limit;
} else {
timeStamp = now;
// 超时后重置
reqCount = 1;
return true;
}
}
public long getNowTime() {
return System.currentTimeMillis();
}
public static void main(String[] args) throws Exception {
CounterTest counterTest = new CounterTest();
while (true) {
Thread.sleep(100);
boolean grant = counterTest.grant();
if (grant) {
System.out.println("接口请求量 ========== : " + counterTest.reqCount);
} else {
System.out.println("接口限流.....");
}
}
}
}
这个算法虽然简单,但是有一个十分致命的问题,那就是临界问题:
假设有一个恶意用户,他在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,那么其实这个用户在 1秒里面,瞬间发送了200个请求。我们刚才规定的是1分钟最多100个请求,也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们的速率限制。用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。