一.限流场景
通过限流,可以控制服务请求速率,从而提高系统应对突发大流量的能力,让系统更具弹性。限流的常见应用场景有商品秒杀活动,12306抢票等。限流对象可以是接口请求频率或者某个IP用户的请求速度等,使其拒绝服务或者排队等待,服务降级等。
二.限流算法
1. 固定窗口
限制指定窗口内(指定时间段内)的请求数不会超过最大值
如10分钟内限制1000个请求
效果有限,无法限制前一个时间段末和后一个时间段始的大量请求
2.滑动窗口
将大窗口划分为粒度更细的小窗口,每次向后移动一个小窗口,并限制大窗口内的请求数不会超过最大值
如10分钟内限制1000个请求,小窗口为1分钟,每次向后移动一分钟,并限制10分钟内请求不会超过1000
相比固定窗口更加平滑(小窗口越小越平滑,性能要求更高),不过没有根本解决突发流量问题

3. 漏桶算法
水流入桶的速度不固定,水以固定速率从桶中流出,如果桶里的水(容器里的请求)已满,则触发限流策略
如设置桶的大小为10000,请求以固定速度为10个/s进行处理
不适合突发流量问题,因为处理速度是固定的
ps:水(请求), 桶(容器),流出(处理)

4. 令牌桶算法
令牌以固定速率加入桶中(不能超过桶最大值),请求处理前需要从桶中获取令牌(如果没有令牌触发限流策略)
如桶大小为10000,令牌以固定速度为10个/s加入桶
能够很好的应对突发流量,因为在此之前桶内存储了大量令牌

public class TokenBucket {
private int availableToken;
private int totality;
private double tokenSpeed;
private long lastTime;
public TokenBucket(int totality, double tokenSpeed) {
this.totality = totality;
this.tokenSpeed = tokenSpeed;
this.lastTime = System.currentTimeMillis();
}
public synchronized boolean getToken(int num) {
fill();
if (num <= availableToken) {
availableToken-=num;
System.out.println(Thread.currentThread().getName() + ": " +"availableToken is :" + availableToken);
return true;
} else {
System.out.println(Thread.currentThread().getName() + ": " +"请等待 : " + availableToken);
return false;
}
}
public void fill() {
long now = System.currentTimeMillis();
if (now > this.lastTime) {
long time = now - this.lastTime;
int newTokens = (int) (time * this.tokenSpeed);
System.out.println(Thread.currentThread().getName() + ": " + newTokens + ", time :" + time);
this.availableToken = Math.min(this.totality, this.availableToken + newTokens);
this.lastTime = now;
}
}
}