计数器法
我们需要选定一个时间起点,之后每当有接口请求到来,我们就将计数器加一。如果在当前时间窗口内,根据限流规则(比如每秒钟最大允许 100 次访问请求),出现累加访问次数超过限流值的情况时,我们就拒绝后续的访问请求。当进入下一个时间窗口之后,计数器就清零重新计数。
public class CounterDemo {public long timeStamp = getNowTime();public int reqCount = 0;public final int limit = 100; // 时间窗口内最大请求数public final long interval = 1000; // 时间窗口mspublic boolean grant() {long now = getNowTime();if (now < timeStamp + interval) {// 在时间窗口内reqCount++;// 判断当前时间窗口内是否超过最大请求控制数return reqCount <= limit;} else {timeStamp = now;// 超时后重置reqCount = 1;return true;}}}
滑动窗口
假设我们的限流规则是,每秒钟不能超过 100 次接口请求。第一个 1s 时间窗口内,100 次接口请求都集中在最后 10ms 内。在第二个 1s 的时间窗口内,100 次接口请求都集中在最开始的 10ms 内。虽然两个时间窗口内流量都符合限流要求(≤100 个请求),但在两个时间窗口临界的 20ms 内,会集中有 200 次接口请求。固定时间窗口限流算法并不能对这种情况做限制,所以,集中在这 20ms 内的 200 次请求就有可能压垮系统。
为了解决这个问题,我们可以对固定时间窗口限流算法稍加改造。我们可以限制任意时间窗口(比如 1s)内,接口请求数都不能超过某个阈值( 比如 100 次)。因此,相对于固定时间窗口限流算法,这个算法叫滑动时间窗口限流算法。
漏桶

public class LeakyDemo {public long timeStamp = getNowTime();public int capacity; // 桶的容量public int rate; // 水漏出的速度public int water; // 当前水量(当前累积请求数)public boolean grant() {long now = getNowTime();water = max(0, water - (now - timeStamp) * rate); // 先执行漏水,计算剩余水量timeStamp = now;if ((water + 1) < capacity) {// 尝试加水,并且水还未满water += 1;return true;} else {// 水满,拒绝加水return false;}}}
令牌桶

public class TokenBucketDemo {public long timeStamp = getNowTime();public int capacity; // 桶的容量public int rate; // 令牌放入速度public int tokens; // 当前令牌数量public boolean grant() {long now = getNowTime();// 先添加令牌tokens = min(capacity, tokens + (now - timeStamp) * rate);timeStamp = now;if (tokens < 1) {// 若不到1个令牌,则拒绝return false;} else {// 还有令牌,领取令牌tokens -= 1;return true;}}}
