Sentinel ParamFlowChecker 的QPS 统计时间是每一个请求到来的时刻而非整秒开始时刻

uujelgoq  于 2021-11-29  发布在  Java
关注(0)|答案(0)|浏览(186)

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>

Anything else we need to know?

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题