令牌桶
令牌桶限流,在SpringCloud分布式下限流,需要把令牌桶放到一个公共的地方,如Zuul路由,guava里有现成的基于令牌桶的限流实现。
令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。 当桶满时,新添加的令牌被丢弃或拒绝。
令牌桶算法是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌。令牌桶算法基本可以用下面的几个概念来描述:
令牌将按照固定的速率被放入令牌桶中。比如每秒放10个。
桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝。
当一个n个字节大小的数据包到达,将从桶中删除n个令牌,接着数据包被发送到网络上。
如果桶中的令牌不足n个,则不会删除令牌,且该数据包将被限流(要么丢弃,要么缓冲区等待)。
令牌算法是根据放令牌的速率去控制输出的速率,也就是上图的to network的速率。to network我们可以理解为消息的处理程序,执行某段业务或者调用某个RPC。
通俗的理解,令牌桶是一个水桶,而令牌是通过一根水管流到水桶中的水
令牌桶的填满时间,是由桶的自身容量、令牌漏出速率(桶下面的水管)、超过平均速率的突发流量持续的时间三个方面共同决定的。如果突发流量的时间比较短,令牌桶不会溢出,在通信流上不会受到影响,如果突发流量比较大,时间比较长,那令牌桶就会溢出,多余的通信流就会被限制。
#### 令牌桶算法和漏桶算法的区别
主要区别在于“漏桶算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输速率外,还允许某种程度的突发传输。在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的门限,因此它适合于具有突发特性的流量。
@Slf4j
@Dependson("asyncTaskExecutor")
@Component
public class RateLimiter{
private Integer limit=10;
private Integer speed=3;
private static volatile Integer tokens=0;
public RateLimiter(){
RateLimiter.tokens=this.limit;
}
@Async("asyncTaskExecutor")
public void asyncTask(){
log.info("RateLimiter Starting...");
while(true){
try{
Thread.sleep(1000L);
int newTokens=tokens+speed;
if(newTokens>limit){
tokens=limit;
}else{
tokens=newTokens;
}
}catch(Exception e){
log.error(ErrorUtil.errorInfoToString(e));
}
}
}
public synchronized boolean execute(){
if(tokens>0){
tokens=tokens-1;
return true;
}
return false;
}
}