限流==保险丝策略,可借助框架如spring cloud中Hystrix组件实现。今天介绍使用guava RateLimiter 类实现接口限流。
漏桶算法
漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率.
两个变量,一个是桶的大小即支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate),伪代码如下:
double rate; // leak rate in calls/s
double burst; // bucket size in calls
long refreshTime; // time for last water refresh
double water; // water count at refreshTime
refreshWater() {
long now = getTimeOfDay();
//水随着时间流逝,不断流走,最多就流干到0.
water = max(0, water - (now - refreshTime) * rate);
refreshTime = now;
}
bool permissionGranted() {
refreshWater();
if (water < burst) {
// 水桶还没满,继续加1
water++;
return true;
}
return false;
}
令牌桶算法
令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样但方向相反,更容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务.
摘自:限流算法之漏桶算法、令牌桶算法
基于令牌桶算法实现,以固定的频率(1秒)向桶中放入一定数量令牌,比如你希望自己的应用程序QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里扔1000个令牌。业务在每次响应请求之前都从桶中获取令牌,只有取到令牌的请求才会被成功响应。获取的方式有两种:阻塞等待令牌或者取不到立即返回失败
非阻塞:立刻返回或doSomethingElse,不等待令牌
If(limiter.tryAcquire()){ //未请求到limiter则立即返回false
doSomething();
}else{
doSomethingElse();
}
阻塞:一直等待获取令牌
final RateLimiter rateLimiter = RateLimiter.create(2.0);
void submitTasks(List tasks, Executor executor) {
for (Runnable task : tasks) {
rateLimiter.acquire(); // may wait
executor.execute(task);
}
}
1、封装限流服务类
@Service
public class AccessLimitService {
//每秒只发出5个令牌 ,超过permits会被阻塞
//类似于JDK的信号量Semphore,用来限制对资源并发访问的线程数
RateLimiter rateLimiter = RateLimiter.create(5.0);
/**
* 尝试获取令牌
* @return
*/
public boolean tryAcquire(){
return rateLimiter.tryAcquire();
}
}
2、限流接口执行业务代码前,先尝试获取令牌
public String access(){
//尝试获取令牌
if(accessLimitService.tryAcquire()){
//模拟业务执行500毫秒
try {
//dosth
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
return "access success";
}
return "access limited";
}