1. 限流算法
通过对并发访问请求进行限速,对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率可以拒绝服务、排队或等待、降级处理
- 计数器
控制单位时间内的请求数量。
劣势:设每分钟请求数量60个,每秒处理1个请求,用户在 00:59 发送 60 个请求,在 01:00 发送 60 个请求 此时 2 秒钟有 120 个请求( 每秒 60 个请求),远远大于了每秒钟处理数量的阈值。(突刺现象)
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
/**
* 最大访问数量
*/
private final int limit = 10;
/**
* 访问时间差
*/
private final long timeout = 1000;
/**
* 请求时间
*/
private long time;
/**
* 当前计数器
*/
private AtomicInteger reqCount = new AtomicInteger(0);
public boolean limit() {
long now = System.currentTimeMillis();
if (now < time + timeout) {
//单位时间内
reqCount.addAndGet(1);
return reqCount.get() <= limit;
} else {
//超出单位时间
time = now;
reqCount = new AtomicInteger(0);
return true;
}
}
}
- 滑动窗口
对计数器的一个改进,增加一个时间粒度的度量单位,一分钟分为若干份(如6份,没10秒一份),在每份上设置独立计数器,
- leaky bucket漏桶
规定固定容量的桶,进入的水无法管控数量、速度,但是对于流出的水我们可以控制速度
劣势:无法应对短时间突发流量(桶满了就丢弃)
public class LeakBucket {
/**
* 时间
*/
private long time;
/**
* 总量
*/
private Double total;
/**
* 水流出速度
*/
private Double rate;
/**
* 当前总量
*/
private Double nowSize;
public boolean limit() {
long now = System.currentTimeMillis();
nowSize = Math.max(0, (nowSize - (now - time) * rate));
time = now;
if ((nowSize + 1) < total) {
nowSize++;
return true;
} else {
return false;
}
}
}
- Token Bucket令牌桶
规定固定容量的桶,token 以固定速度往桶内填充, 当桶满时 token 不会被继续放入, 每过来一个请求把 token 从桶中移除, 如果桶中没有
token 不能请求。
可以准备一个队列,用来保存令牌,另外通过一个线程池定期生成令牌放到队列中,每来一个请求,就从队列中获取一个令牌,并继续执行。
public class TokenBucket {
/**
* 时间
*/
private long time;
/**
* 总量
*/
private Double total;
/**
* 水流出速度
*/
private Double rate;
/**
* 当前总量
*/
private Double nowSize;
public boolean limit() {
long now = System.currentTimeMillis();
nowSize = Math.min(total, (nowSize + (now - time) * rate));
time = now;
if (nowSize < 1) {
//桶里没有token
return false;
} else {
//存在token
nowSize -= 1;
return true;
}
}
}