Sentinel的流控效果有四种类型,都是TrafficShapingController的实现类,分别是DefaultController(默认策略,直接拒绝),WarmUpController(预热启动),RateLimiterController(排队等待),
WarmUpRateLimiterController(预热排队等待)。
DefaultController.canPass(直接拒绝,如果限流允许优先级,并且是基于QPS限流,尝试抢占未来的时间窗口)
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
//获取当前时间窗口的请求数(或者线程数)
int curCount = this.avgUsedTokens(node);
if((double)(curCount + acquireCount) > this.count) {//达到了限流的限制
//存在优先级,并且是基于QPS的限流阀值类型
if(prioritized && this.grade == 1) {
long currentTime = TimeUtil.currentTimeMillis();
//计算离未来时间窗口需要等待的时间
long waitInMs = node.tryOccupyNext(currentTime, acquireCount, this.count);
if(waitInMs < (long)OccupyTimeoutProperty.getOccupyTimeout()) {//小于最大占用超时时间
//抢占未来的时间窗口
node.addWaitingRequest(currentTime + waitInMs, acquireCount);
node.addOccupiedPass(acquireCount);
//睡眠等待到达未来的时间窗口
this.sleep(waitInMs);
throw new PriorityWaitException(waitInMs);
}
}
return false;
} else {
return true;
}
}
private int avgUsedTokens(Node node) {
return node == null?0:(this.grade == 0?node.curThreadNum():(int)node.passQps());
}
public long tryOccupyNext(long currentTime, int acquireCount, double threshold) {
//计算一秒内可以通过的请求数
double maxCount = threshold * (double)IntervalProperty.INTERVAL / 1000.0D;
//获取未来时间窗口的请求数
long currentBorrow = this.rollingCounterInSecond.waiting();
if((double)currentBorrow >= maxCount) {//未来时间窗口的请求数大于超过限制
//返回最大占用超时时间
return (long)OccupyTimeoutProperty.getOccupyTimeout();
} else {
//窗口间隔长度
int windowLength = IntervalProperty.INTERVAL / SampleCountProperty.SAMPLE_COUNT;
//计算上一个时间窗口开始的时间
long earliestTime = currentTime - currentTime % (long)windowLength + (long)windowLength - (long)IntervalProperty.INTERVAL;
int idx = 0;
//通过的请求数
for(long currentPass = this.rollingCounterInSecond.pass(); earliestTime < currentTime; ++idx) {
//当前时间窗口剩余的时间
long waitInMs = (long)(idx * windowLength + windowLength) - currentTime % (long)windowLength;
if(waitInMs >= (long)OccupyTimeoutProperty.getOccupyTimeout()) {
break;
}
//当前时间的上一个时间窗口的请求通过数
long windowPass = this.rollingCounterInSecond.getWindowPass(earliestTime);
if((double)(currentPass + currentBorrow + (long)acquireCount - windowPass) <= maxCount) {
return waitInMs;
}
//后一个时间窗口的开始时间
earliestTime += (long)windowLength;
currentPass -= windowPass;
}
return (long)OccupyTimeoutProperty.getOccupyTimeout();
}
}
public long currentWaiting() {
this.borrowArray.currentWindow();
long currentWaiting = 0L;
List list = this.borrowArray.values();
MetricBucket window;
for(Iterator var4 = list.iterator(); var4.hasNext(); currentWaiting += window.pass()) {
window = (MetricBucket)var4.next();
}
return currentWaiting;
}
WarmUpController.canPass(预热启动)
查看WarmUpController的构造函数
public WarmUpController(double count, int warmUpPeriodInSec) { this.construct(count, warmUpPeriodInSec, 3); }
private void construct(double count, int warmUpPeriodInSec, int coldFactor) { if(coldFactor <= 1) { throw new IllegalArgumentException("Cold factor should be larger than 1"); } else { this.count = count; this.coldFactor = coldFactor; //剩余token的警戒值,warmUpPeriodInSec(预热时长秒)默认为FlowRule的warmUpPeriodInSec(10)
//coldFactor 冷启动系数 默认为3,所以当count=1时,warningToken默认为5,maxToken默认为10,slope为0.4
this.warningToken = (int)((double)warmUpPeriodInSec * count) / (coldFactor - 1); this.maxToken = this.warningToken + (int)((double)(2 * warmUpPeriodInSec) * count / (1.0D + (double)coldFactor)); this.slope = ((double)coldFactor - 1.0D) / count / (double)(this.maxToken - this.warningToken); } }
public boolean canPass(Node node, int acquireCount, boolean prioritized) { //每一秒钟通过的请求数 long passQps = (long)node.passQps(); //上一个时间窗口通过的请求数 long previousQps = (long)node.previousPassQps(); //计算剩余token数 this.syncToken(previousQps); long restToken = this.storedTokens.get(); if(restToken >= (long)this.warningToken) { //计算超出警戒值的token数 long aboveToken = restToken - (long)this.warningToken; //计算当前允许通过的请求数 double warningQps = Math.nextUp(1.0D / ((double)aboveToken * this.slope + 1.0D / this.count)); if((double)(passQps + (long)acquireCount) <= warningQps) { return true; } //预热结束,直接判断是否达到限流数 } else if((double)(passQps + (long)acquireCount) <= this.count) { return true; } return false; }
protected void syncToken(long passQps) { long currentTime = TimeUtil.currentTimeMillis(); currentTime -= currentTime % 1000L; long oldLastFillTime = this.lastFilledTime.get(); if(currentTime > oldLastFillTime) { long oldValue = this.storedTokens.get(); //获得新的令牌数 long newValue = this.coolDownTokens(currentTime, passQps); if(this.storedTokens.compareAndSet(oldValue, newValue)) { //减去上次通过的qps数量,得到当前的实际token数 long currentValue = this.storedTokens.addAndGet(0L - passQps); if(currentValue < 0L) { this.storedTokens.set(0L); } this.lastFilledTime.set(currentTime); } } }
private long coolDownTokens(long currentTime, long passQps) { //上一次剩余的token数,初始值为0 long oldValue = this.storedTokens.get(); long newValue = oldValue; //系统初始启动阶段 if(oldValue < (long)this.warningToken) { //初始阶段,oldValue =0,lastFilledTime=0,currentTime 很大,newValue 会得到一个很大的值 newValue = (long)((double)oldValue + (double)(currentTime - this.lastFilledTime.get()) * this.count / 1000.0D); //预热阶段,当前qps小于 count / coldFactor } else if(oldValue > (long)this.warningToken && passQps < (long)((int)this.count / this.coldFactor)) { newValue = (long)((double)oldValue + (double)(currentTime - this.lastFilledTime.get()) * this.count / 1000.0D); } return Math.min(newValue, (long)this.maxToken); }
RateLimiterController.canPass(排队等待)
public boolean canPass(Node node, int acquireCount, boolean prioritized) { if(acquireCount <= 0) { return true; } else if(this.count <= 0.0D) { return false; } else { long currentTime = TimeUtil.currentTimeMillis(); //计算请求的间隔时间 long costTime = Math.round(1.0D * (double)acquireCount / this.count * 1000.0D); //本次期望通过的时间 long expectedTime = costTime + this.latestPassedTime.get(); if(expectedTime <= currentTime) {//期望通过时间小于当前时间,不需要限流 this.latestPassedTime.set(currentTime); return true; } else { //需要等待的时间 long waitTime = costTime + this.latestPassedTime.get() - TimeUtil.currentTimeMillis(); if(waitTime > (long)this.maxQueueingTimeMs) { return false; } else { //计算通过时间 long oldTime = this.latestPassedTime.addAndGet(costTime); try { //重新计算等待时间,防止并发访问 waitTime = oldTime - TimeUtil.currentTimeMillis(); if(waitTime > (long)this.maxQueueingTimeMs) {//等待时间过长,返回失败 this.latestPassedTime.addAndGet(-costTime); return false; } else { if(waitTime > 0L) { //睡眠等待 Thread.sleep(waitTime); } return true; } } catch (InterruptedException var15) { return false; } } } } }