问题描述
昨天写的笔记中,最后我留了一个疑问,疑问是这样的: 假如:在0-500ms之间有4次请求,500-1000ms之间有4个请求;在1000ms - 1500ms之间请求次数为0,1600ms的时候,有三个请求进来,此时
在进行判断的时候,会把第二个窗口进行reset:也就变成了这样:
我的疑问是:此时在统计qps的时候,如果还是直接把两个窗口的请求次数累加到一起,那就统计的不是前1S之内的请求数了;、 今天翻了下源码,发现针对这个场景的源码了:
源码
com.alibaba.csp.sentinel.node.StatisticNode#successQps
这个类中,是获取相关统计指标的收口类,就是说,不管是限流、降级还是其他场景需要获取相关的qps指标,都需要通过这个类实现,所以我们找其中的一个方法看下实现源码:
public double successQps() {
return rollingCounterInSecond.success() / rollingCounterInSecond.getWindowIntervalInSec();
}
要统计qps,当然是同success的数量 / 1S rollingCounterInSecond.getWindowIntervalInSec() 这个不贴代码了,里面就是用1000ms / 1000ms;这个计算方法的意思是说:如果在初始化时,预设的时间窗口是1000ms,那就除以1000ms 得到对应的秒,得到1S,也就是说当前窗口中统计的是1S内的请求数量 关键要看的是rollingCounterInSecond.success()
public long success() {
data.currentWindow();
long success = 0;
List<MetricBucket> list = data.values();
for (MetricBucket window : list) {
success += window.success();
}
return success;
}
这个方法大致的意思:就是说获取到当前所有的窗口,把窗口中success的数量累计起来,关键的代码,在data.values();
public List<T> values(long timeMillis) {
if (timeMillis < 0) {
return new ArrayList<T>();
}
int size = array.length();
List<T> result = new ArrayList<T>(size);
for (int i = 0; i < size; i++) {
WindowWrap<T> windowWrap = array.get(i);
if (windowWrap == null || isWindowDeprecated(timeMillis, windowWrap)) {
continue;
}
result.add(windowWrap.value());
}
return result;
}
public boolean isWindowDeprecated(long time, WindowWrap<T> windowWrap) {
return time - windowWrap.windowStart() > intervalInMs;
}
这段代码可以看到,会依次遍历每个窗口,如果当前时间 - 窗口的起始时间,大于qps统计的时间1000ms,就不统计,那上面的场景,就可以解释了:
窗口0在统计的时候,由于1600ms - 0ms > 1000ms 所以0-500ms之间的4次请求是不会被统计的 窗口1在统计的时候,1600 - 1500ms < 1000ms ,所以会被统计进去
结论
所以:结论就是,虽然这种场景也是有两个窗口,但是sentinel在统计的时候,会过滤当前时间和窗口起始时间在1000ms之内的所有窗口,统计对应的请求次数
|