Issue Description
ParamFlowChecker
的QPS 统计时间是每一个请求到来的时刻而非整秒开始时刻
在方法passDefaultLocalCheck(..)
中,以下代码片段(忽略了无关代码片段)。
...
...
while (true) {
long currentTime = TimeUtil.currentTimeMillis();
AtomicLong lastAddTokenTime = timeCounters.putIfAbsent(value, new AtomicLong(currentTime));
if (lastAddTokenTime == null) {
// Token never added, just replenish the tokens and consume {@code acquireCount} immediately.
tokenCounters.putIfAbsent(value, new AtomicLong(maxCount - acquireCount));
return true;
}
// Calculate the time duration since last token was added.
long passTime = currentTime - lastAddTokenTime.get();
// A simplified token bucket algorithm that will replenish the tokens only when statistic window has passed.
if (passTime > rule.getDurationInSec() * 1000) {
AtomicLong oldQps = tokenCounters.putIfAbsent(value, new AtomicLong(maxCount - acquireCount));
if (oldQps == null) {
// Might not be accurate here.
lastAddTokenTime.set(currentTime);
return true;
} else {
...
...
if (oldQps.compareAndSet(restQps, newQps)) {
lastAddTokenTime.set(currentTime);
return true;
}
Thread.yield();
}
} else {
...
...
}
}
从三行代码中可以看出,统计的开始时间并没有做处理,直接使用了当前时间(currentTime
)
156 行
AtomicLong lastAddTokenTime = timeCounters.putIfAbsent(value, new AtomicLong(currentTime));
170 行以及182行
lastAddTokenTime.set(currentTime);
这样的统计与限流似乎也没什么问题,但是在dashboard
控制台的实时监控那里就会有问题,两者不匹配。
因为控制台中的实时监控那里取的统计数据是通过http 请求,在客户端中由SendMetricCommandHandler
实现。
而在客户端这边的读取(SendMetricCommandHandler
)是从日志文件metrics.log
中获取,该文件中的数据是由MetricWriter
定时写入的,定时任务时从ClusterNode
中取出数据并写入日志文件。实际的统计数据可查看StatisticNode
,而它统计数据是按整秒([0.000, 1.000))统计的。
这样在实时监控看到的统计数据有时候就会超出我们设置的QPS 非常多。
就是因为这里,实时监控查看到的统计数据是整秒的统计数据。而ParamFlowChecker
统计的时间并非整秒,而是首次到来的请求的时间点。
我想问一下,是机制就是这样设计的还是说它就是一个BUG 呢?
Type: bug report or feature request
bug report
Describe what happened (or what feature you want)
Describe what you expected to happen
对关键的三行代码做如下调整,就可以解决该问题。
// 开始时间控制到整秒,与统计时间窗口起始时间一致。
long startTime = currentTime - currentTime % 1000;
AtomicLong lastAddTokenTime = timeCounters.putIfAbsent(value, new AtomicLong(startTime));
...
lastAddTokenTime.set(startTime);
...
lastAddTokenTime.set(startTime);
我本来想是否可以使用StatisticNode
来进行统计,但是要变更和修改的代码太多了,这样的变更影响最小,如果有问题回溯也会简单一点。
How to reproduce it (as minimally and precisely as possible)
Tell us your environment
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.8.1</version>
</dependency>
暂无答案!
目前还没有任何答案,快来回答吧!