上一篇文章微服务中常用的限流算法(一)中我们介绍了滑动窗口算法和滚动窗口算法和具体的实现代码,本篇文章我们介绍漏桶限流算法和令牌桶限流算法。
漏桶限流算法
漏桶限流算法是模拟水流过一个有漏洞的桶进而限流的思路。
水龙头的水先流入漏桶,再通过漏桶底部的孔流出。如果流入的水量太大,底部的孔来不及流出,就会导致水桶太满溢出去。限流器利用漏桶的这个原理设计漏桶限流算法,用户请求先流入到一个特定大小的漏桶中,系统以特定的速率从漏桶中获取请求并处理。如果用户请求超过限流,就会导致漏桶被请求数据填满,请求溢出,返回 503 响应。所以漏桶算法不仅可以限流,当流量超过限制的时候回拒绝处理,直接返回503响应,还能控制请求处理的速度,漏桶限流算法示意图如下:
在实现上,根据请求处理的事件间隔,例如:限流每秒10个请求,那么两个请求处理的时间间隔必须>=100ms,每个用户请求到达限流器后,根据当前最近一个请求处理的时间和最色的请求线程数目,计算当前线程sleep的时间,每个请求线程sleep的时间不同,最后就可以可以实现每隔100ms唤醒一个请求线程处理请求,从而达到漏桶限流的效果。漏桶限流限流算法的实现代码如下(父类定义在上一篇文章中):
public class LeakBucket extends AbstractLimitAlgo {
private int interval;
private int blockTask = 0;
public LeakBucket(int limit,int interval) {
super(limit);
this.interval = interval;
}
@Override
public boolean tryAcquire() {
if(getElapseTimeFromLast() >= interval && blockTask <= 0) {
lastTimeStamp = System.currentTimeMillis();
return true;
}
if(blockTask > limit) {
return false;
}
blockTask++;
long sleep = interval * (++blockTask) - getElapseTimeFromLast();
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
lastTimeStamp = System.currentTimeMillis();
blockTask--;
return true;
}
}
细心的老铁会发现,漏桶算法的限流策略,只能使请求按照一定的时间间隔处理,即使当前资源比较空闲,漏桶也只能慢慢的一个一个的处理,这其实不太符合人们的期望,因为这样就是在浪费计算资源。上述漏桶算法实现代码,存在一定的线程安全问题,如果要保证线程安全,需要增加一些额外的代码,反而使算法实现变得更加复杂,本文重点说明算法思路,对于线程安全问题,读者可以自行在本地实现。
令牌桶限流算法
令牌桶是另一种桶限流算法,模拟一个特定大小的桶,然后向桶中以特定的速度放入令牌(token),请求到达后,必须从桶中取出一个令牌才能继续处理。如果桶中已经没有令牌了,那么当前请求就被限流,返回 503 响应。
上面的算法描述似乎需要有一个专门线程生成令牌,还需要一个数据结构模拟桶。实际上,令牌桶的实现,只需要在请求获取令牌的时候,通过时间计算,就可以算出令牌桶中的总令牌数,实现代码如下:
public class TokenBucket extends AbstractLimitAlgo {
private int limit;
private int count = 0;
private long lastTimeStamp ;
// 流速控制
private long interval;
public TokenBucket(int limit, long interval) {
super(limit);
this.interval = interval;
}
@Override
public boolean tryAcquire() {
if(count >= 1) {
count--;
return true;
}
count= Math.min(limit,count + Long.valueOf(getElapseTimeFromLast() / interval).intValue());
if(count >= 1) {
count--;
return true;
}
return false;
}
}
令牌桶限流算法,可以根据桶内剩余令牌的多少,来影响请求处理的速度,例如一段时间内,没有用户请求,使令牌桶内积攒一部分令牌 ,那么此时 如果来了一波流量的话,那么可以在短时间内,获取到桶内所有令牌,是这波流量同时被处理,也就是说,使用令牌桶限流算法,并不会限制请求处理的速度,后端服务处理请求的峰值为令牌桶的容量。所以在设置令牌桶容量时,可以参考后端服务可以处理流量的峰值。同样令牌桶算法的实现代码也存在线程安全问题,读者可自行解决线程安全问题。
令牌桶限流算法综合效果比较好,能在最大程度利用系统资源处理请求的基础上,实现限流的目标。
总结
小编用了两篇文章 微服务中常用的限流算法(一) 和 微服务中常用的限流算法(二),讨论了在微服务中常用的限流算法和对应的代码实现,除了上文中提到的四种限流算法外,你还只知道哪些限流算法吗?