Refactor the constructor and units of LeapArray and related statistic class
- The constructor now accept `sampleCount` and `windowIntervalMs` so that it can match the two basic properties Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
c7c59e9de7
commit
a2b91a9030
|
|
@ -18,25 +18,27 @@ package com.alibaba.csp.sentinel.node;
|
|||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||
import com.alibaba.csp.sentinel.property.SimplePropertyListener;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||
|
||||
/***
|
||||
/**
|
||||
* QPS statistics interval.
|
||||
*
|
||||
* @author youji.zj
|
||||
* @author jialiang.linjl
|
||||
* @author CarpenterLee
|
||||
* @author Carpenter Lee
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class IntervalProperty {
|
||||
|
||||
/**
|
||||
* <p>Interval in milliseconds. This variable determines sensitivity of the QPS calculation.</p>
|
||||
* <p>
|
||||
* Interval in seconds. This variable determines sensitivity of the QPS calculation.
|
||||
* </p>
|
||||
* DO NOT MODIFY this value directly, use {@link #updateInterval(int)}, otherwise the modification will not
|
||||
* take effect.
|
||||
* </p>
|
||||
*/
|
||||
public static volatile int INTERVAL = 1;
|
||||
public static volatile int INTERVAL = RuleConstant.DEFAULT_WINDOW_INTERVAL_MS;
|
||||
|
||||
public static void register2Property(SentinelProperty<Integer> property) {
|
||||
property.addListener(new SimplePropertyListener<Integer>() {
|
||||
|
|
@ -60,7 +62,7 @@ public class IntervalProperty {
|
|||
INTERVAL = newInterval;
|
||||
ClusterBuilderSlot.resetClusterNodes();
|
||||
}
|
||||
RecordLog.info("INTERVAL updated to: " + INTERVAL);
|
||||
RecordLog.info("[IntervalProperty] INTERVAL updated to: " + INTERVAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,14 +92,14 @@ public class StatisticNode implements Node {
|
|||
* Holds statistics of the recent {@code INTERVAL} seconds. The {@code INTERVAL} is divided into time spans
|
||||
* by given {@code sampleCount}.
|
||||
*/
|
||||
private transient volatile Metric rollingCounterInSecond = new ArrayMetric(1000 / SampleCountProperty.SAMPLE_COUNT,
|
||||
private transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,
|
||||
IntervalProperty.INTERVAL);
|
||||
|
||||
/**
|
||||
* Holds statistics of the recent 60 seconds. The windowLengthInMs is deliberately set to 1000 milliseconds,
|
||||
* meaning each bucket per second, in this way we can get accurate statistics of each second.
|
||||
*/
|
||||
private transient Metric rollingCounterInMinute = new ArrayMetric(1000, 60);
|
||||
private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000);
|
||||
|
||||
/**
|
||||
* The counter for thread count.
|
||||
|
|
@ -142,7 +142,7 @@ public class StatisticNode implements Node {
|
|||
|
||||
@Override
|
||||
public void reset() {
|
||||
rollingCounterInSecond = new ArrayMetric(1000 / SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL);
|
||||
rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -158,7 +158,7 @@ public class StatisticNode implements Node {
|
|||
|
||||
@Override
|
||||
public long blockQps() {
|
||||
return rollingCounterInSecond.block() / IntervalProperty.INTERVAL;
|
||||
return rollingCounterInSecond.block() / (long) rollingCounterInSecond.getWindowIntervalInSec();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -183,7 +183,7 @@ public class StatisticNode implements Node {
|
|||
|
||||
@Override
|
||||
public long exceptionQps() {
|
||||
return rollingCounterInSecond.exception() / IntervalProperty.INTERVAL;
|
||||
return rollingCounterInSecond.exception() / (long) rollingCounterInSecond.getWindowIntervalInSec();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -193,17 +193,17 @@ public class StatisticNode implements Node {
|
|||
|
||||
@Override
|
||||
public long passQps() {
|
||||
return rollingCounterInSecond.pass() / IntervalProperty.INTERVAL;
|
||||
return rollingCounterInSecond.pass() / (long) rollingCounterInSecond.getWindowIntervalInSec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long successQps() {
|
||||
return rollingCounterInSecond.success() / IntervalProperty.INTERVAL;
|
||||
return rollingCounterInSecond.success() / (long) rollingCounterInSecond.getWindowIntervalInSec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long maxSuccessQps() {
|
||||
return rollingCounterInSecond.maxSuccess() * SampleCountProperty.SAMPLE_COUNT;
|
||||
return rollingCounterInSecond.maxSuccess() * rollingCounterInSecond.getSampleCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -51,5 +51,8 @@ public final class RuleConstant {
|
|||
public static final String LIMIT_APP_DEFAULT = "default";
|
||||
public static final String LIMIT_APP_OTHER = "other";
|
||||
|
||||
public static final int DEFAULT_SAMPLE_COUNT = 2;
|
||||
public static final int DEFAULT_WINDOW_INTERVAL_MS = 1000;
|
||||
|
||||
private RuleConstant() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,20 +54,17 @@ public abstract class LeapArray<T> {
|
|||
/**
|
||||
* The total bucket count is: {@code sampleCount = intervalInMs / windowLengthInMs}.
|
||||
*
|
||||
* @param windowLengthInMs a single window bucket's time length in milliseconds.
|
||||
* @param intervalInSec the total time span of this {@link LeapArray} in seconds.
|
||||
* @param sampleCount bucket count of the sliding window
|
||||
* @param intervalInMs the total time interval of this {@link LeapArray} in milliseconds
|
||||
*/
|
||||
public LeapArray(int windowLengthInMs, int intervalInSec) {
|
||||
// TODO: change `intervalInSec` to `intervalInMs`
|
||||
AssertUtil.isTrue(windowLengthInMs > 0, "bucket length is invalid: " + windowLengthInMs);
|
||||
int intervalInMs = intervalInSec * 1000;
|
||||
AssertUtil.isTrue(intervalInMs > windowLengthInMs,
|
||||
"total time span of the window should be greater than bucket length");
|
||||
AssertUtil.isTrue(intervalInMs % windowLengthInMs == 0, "time span needs to be evenly divided");
|
||||
public LeapArray(int sampleCount, int intervalInMs) {
|
||||
AssertUtil.isTrue(sampleCount > 0, "bucket count is invalid: " + sampleCount);
|
||||
AssertUtil.isTrue(intervalInMs > 0, "total time interval of the sliding window should be positive");
|
||||
AssertUtil.isTrue(intervalInMs % sampleCount == 0, "time span needs to be evenly divided");
|
||||
|
||||
this.windowLengthInMs = windowLengthInMs;
|
||||
this.windowLengthInMs = intervalInMs / sampleCount;
|
||||
this.intervalInMs = intervalInMs;
|
||||
this.sampleCount = intervalInMs / windowLengthInMs;
|
||||
this.sampleCount = sampleCount;
|
||||
|
||||
this.array = new AtomicReferenceArray<WindowWrap<T>>(sampleCount);
|
||||
}
|
||||
|
|
@ -345,12 +342,21 @@ public abstract class LeapArray<T> {
|
|||
return sampleCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total interval length of the sliding window in milliseconds.
|
||||
*
|
||||
* @return interval in second
|
||||
*/
|
||||
public int getIntervalInMs() {
|
||||
return intervalInMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total interval length of the sliding window.
|
||||
*
|
||||
* @return interval in second
|
||||
*/
|
||||
public int getIntervalInSecond() {
|
||||
return intervalInMs / 1000;
|
||||
public double getIntervalInSecond() {
|
||||
return intervalInMs / 1000.0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,12 +33,8 @@ public class ArrayMetric implements Metric {
|
|||
|
||||
private final MetricsLeapArray data;
|
||||
|
||||
/**
|
||||
* @param windowLengthInMs a single window bucket's time length in milliseconds.
|
||||
* @param intervalInSec the total time span of this {@link ArrayMetric} in seconds.
|
||||
*/
|
||||
public ArrayMetric(int windowLengthInMs, int intervalInSec) {
|
||||
this.data = new MetricsLeapArray(windowLengthInMs, intervalInSec);
|
||||
public ArrayMetric(int sampleCount, int intervalInMs) {
|
||||
this.data = new MetricsLeapArray(sampleCount, intervalInMs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -229,4 +225,14 @@ public class ArrayMetric implements Metric {
|
|||
}
|
||||
return wrap.value().pass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWindowIntervalInSec() {
|
||||
return data.getIntervalInSecond();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSampleCount() {
|
||||
return data.getSampleCount();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,10 @@ public interface Metric {
|
|||
*/
|
||||
void addRT(long rt);
|
||||
|
||||
double getWindowIntervalInSec();
|
||||
|
||||
int getSampleCount();
|
||||
|
||||
// Tool methods.
|
||||
|
||||
void debugQps();
|
||||
|
|
|
|||
|
|
@ -28,12 +28,8 @@ import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
|
|||
*/
|
||||
public class MetricsLeapArray extends LeapArray<MetricBucket> {
|
||||
|
||||
/**
|
||||
* @param windowLengthInMs a single window bucket's time length in milliseconds.
|
||||
* @param intervalInSec the total time span of this {@link MetricsLeapArray} in seconds.
|
||||
*/
|
||||
public MetricsLeapArray(int windowLengthInMs, int intervalInSec) {
|
||||
super(windowLengthInMs, intervalInSec);
|
||||
public MetricsLeapArray(int sampleCount, int intervalInMs) {
|
||||
super(sampleCount, intervalInMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import static org.mockito.Mockito.*;
|
|||
public class ArrayMetricTest {
|
||||
|
||||
private final int windowLengthInMs = 500;
|
||||
private final int intervalInSec = 1;
|
||||
|
||||
@Test
|
||||
public void testOperateArrayMetric() {
|
||||
|
|
|
|||
|
|
@ -39,10 +39,12 @@ public class MetricsLeapArrayTest {
|
|||
|
||||
private final int windowLengthInMs = 1000;
|
||||
private final int intervalInSec = 2;
|
||||
private final int intervalInMs = intervalInSec * 1000;
|
||||
private final int sampleCount = intervalInMs / windowLengthInMs;
|
||||
|
||||
@Test
|
||||
public void testNewWindow() {
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(sampleCount, intervalInMs);
|
||||
long time = TimeUtil.currentTimeMillis();
|
||||
WindowWrap<MetricBucket> window = leapArray.currentWindow(time);
|
||||
|
||||
|
|
@ -54,7 +56,7 @@ public class MetricsLeapArrayTest {
|
|||
|
||||
@Test
|
||||
public void testLeapArrayWindowStart() {
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(sampleCount, intervalInMs);
|
||||
long firstTime = TimeUtil.currentTimeMillis();
|
||||
long previousWindowStart = firstTime - firstTime % windowLengthInMs;
|
||||
|
||||
|
|
@ -66,7 +68,7 @@ public class MetricsLeapArrayTest {
|
|||
|
||||
@Test
|
||||
public void testWindowAfterOneInterval() {
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(sampleCount, intervalInMs);
|
||||
long firstTime = TimeUtil.currentTimeMillis();
|
||||
long previousWindowStart = firstTime - firstTime % windowLengthInMs;
|
||||
WindowWrap<MetricBucket> window = leapArray.currentWindow(previousWindowStart);
|
||||
|
|
@ -106,8 +108,8 @@ public class MetricsLeapArrayTest {
|
|||
|
||||
@Deprecated
|
||||
public void testWindowDeprecatedRefresh() {
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
|
||||
final int len = intervalInSec * 1000 / windowLengthInMs;
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(sampleCount, intervalInMs);
|
||||
final int len = sampleCount;
|
||||
long firstTime = TimeUtil.currentTimeMillis();
|
||||
List<WindowWrap<MetricBucket>> firstIterWindowList = new ArrayList<WindowWrap<MetricBucket>>(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
|
@ -126,7 +128,7 @@ public class MetricsLeapArrayTest {
|
|||
public void testMultiThreadUpdateEmptyWindow() throws Exception {
|
||||
final long time = TimeUtil.currentTimeMillis();
|
||||
final int nThreads = 16;
|
||||
final MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
|
||||
final MetricsLeapArray leapArray = new MetricsLeapArray(sampleCount, intervalInMs);
|
||||
final CountDownLatch latch = new CountDownLatch(nThreads);
|
||||
Runnable task = new Runnable() {
|
||||
@Override
|
||||
|
|
@ -147,7 +149,7 @@ public class MetricsLeapArrayTest {
|
|||
|
||||
@Test
|
||||
public void testGetPreviousWindow() {
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(sampleCount, intervalInMs);
|
||||
long time = TimeUtil.currentTimeMillis();
|
||||
WindowWrap<MetricBucket> previousWindow = leapArray.currentWindow(time);
|
||||
assertNull(leapArray.getPreviousWindow(time));
|
||||
|
|
@ -162,10 +164,10 @@ public class MetricsLeapArrayTest {
|
|||
@Test
|
||||
public void testListWindowsResetOld() throws Exception {
|
||||
final int windowLengthInMs = 100;
|
||||
final int intervalInSec = 1;
|
||||
final int intervalInMs = intervalInSec * 1000;
|
||||
final int intervalInMs = 1000;
|
||||
final int sampleCount = intervalInMs / windowLengthInMs;
|
||||
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(sampleCount, intervalInMs);
|
||||
long time = TimeUtil.currentTimeMillis();
|
||||
|
||||
Set<WindowWrap<MetricBucket>> windowWraps = new HashSet<WindowWrap<MetricBucket>>();
|
||||
|
|
@ -190,8 +192,10 @@ public class MetricsLeapArrayTest {
|
|||
public void testListWindowsNewBucket() throws Exception {
|
||||
final int windowLengthInMs = 100;
|
||||
final int intervalInSec = 1;
|
||||
final int intervalInMs = intervalInSec * 1000;
|
||||
final int sampleCount = intervalInMs / windowLengthInMs;
|
||||
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(windowLengthInMs, intervalInSec);
|
||||
MetricsLeapArray leapArray = new MetricsLeapArray(sampleCount, intervalInMs);
|
||||
long time = TimeUtil.currentTimeMillis();
|
||||
|
||||
Set<WindowWrap<MetricBucket>> windowWraps = new HashSet<WindowWrap<MetricBucket>>();
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ public class LeapArrayTest {
|
|||
@Test
|
||||
public void testGetValidHead() {
|
||||
int windowLengthInMs = 100;
|
||||
int intervalInSec = 1;
|
||||
int sampleCount = intervalInSec * 1000 / windowLengthInMs;
|
||||
LeapArray<AtomicInteger> leapArray = new LeapArray<AtomicInteger>(windowLengthInMs, intervalInSec) {
|
||||
int intervalInMs = 1000;
|
||||
int sampleCount = intervalInMs / windowLengthInMs;
|
||||
LeapArray<AtomicInteger> leapArray = new LeapArray<AtomicInteger>(sampleCount, intervalInMs) {
|
||||
@Override
|
||||
public AtomicInteger newEmptyBucket() {
|
||||
return new AtomicInteger(0);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
package com.alibaba.csp.sentinel.slots.block.flow.param;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.alibaba.csp.sentinel.log.RecordLog;
|
|||
import com.alibaba.csp.sentinel.node.IntervalProperty;
|
||||
import com.alibaba.csp.sentinel.node.SampleCountProperty;
|
||||
import com.alibaba.csp.sentinel.slots.statistic.metric.HotParameterLeapArray;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* Metrics for frequent ("hot spot") parameters.
|
||||
|
|
@ -34,6 +35,21 @@ import com.alibaba.csp.sentinel.slots.statistic.metric.HotParameterLeapArray;
|
|||
*/
|
||||
public class ParameterMetric {
|
||||
|
||||
private final int sampleCount;
|
||||
private final int intervalMs;
|
||||
|
||||
public ParameterMetric() {
|
||||
this(SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL);
|
||||
}
|
||||
|
||||
public ParameterMetric(int sampleCount, int intervalInMs) {
|
||||
AssertUtil.isTrue(sampleCount > 0, "sampleCount should be positive");
|
||||
AssertUtil.isTrue(intervalInMs > 0, "window interval should be positive");
|
||||
AssertUtil.isTrue(intervalInMs % sampleCount == 0, "time span needs to be evenly divided");
|
||||
this.sampleCount = sampleCount;
|
||||
this.intervalMs = intervalInMs;
|
||||
}
|
||||
|
||||
private Map<Integer, HotParameterLeapArray> rollingParameters =
|
||||
new ConcurrentHashMap<Integer, HotParameterLeapArray>();
|
||||
|
||||
|
|
@ -50,8 +66,7 @@ public class ParameterMetric {
|
|||
synchronized (this) {
|
||||
// putIfAbsent
|
||||
if (rollingParameters.get(index) == null) {
|
||||
rollingParameters.put(index, new HotParameterLeapArray(
|
||||
1000 / SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL));
|
||||
rollingParameters.put(index, new HotParameterLeapArray(sampleCount, intervalMs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,15 +37,8 @@ import com.alibaba.csp.sentinel.slots.statistic.data.ParamMapBucket;
|
|||
*/
|
||||
public class HotParameterLeapArray extends LeapArray<ParamMapBucket> {
|
||||
|
||||
private int intervalInSec;
|
||||
|
||||
public HotParameterLeapArray(int windowLengthInMs, int intervalInSec) {
|
||||
super(windowLengthInMs, intervalInSec);
|
||||
this.intervalInSec = intervalInSec;
|
||||
}
|
||||
|
||||
public int getIntervalInSec() {
|
||||
return intervalInSec;
|
||||
public HotParameterLeapArray(int sampleCount, int intervalInMs) {
|
||||
super(sampleCount, intervalInMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -116,7 +109,7 @@ public class HotParameterLeapArray extends LeapArray<ParamMapBucket> {
|
|||
if (x.getValue() == 0) {
|
||||
break;
|
||||
}
|
||||
doubleResult.put(x.getKey(), ((double)x.getValue()) / getIntervalInSec());
|
||||
doubleResult.put(x.getKey(), ((double)x.getValue()) / getIntervalInSecond());
|
||||
}
|
||||
|
||||
return doubleResult;
|
||||
|
|
@ -136,6 +129,6 @@ public class HotParameterLeapArray extends LeapArray<ParamMapBucket> {
|
|||
}
|
||||
|
||||
public double getRollingAvg(RollingParamEvent event, Object value) {
|
||||
return ((double) getRollingSum(event, value)) / getIntervalInSec();
|
||||
return ((double) getRollingSum(event, value)) / getIntervalInSecond();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public class HotParameterLeapArrayTest {
|
|||
int a1 = 3, a2 = 5;
|
||||
String paramPrefix = "param-";
|
||||
HotParameterLeapArray leapArray = mock(HotParameterLeapArray.class);
|
||||
when(leapArray.getIntervalInSec()).thenReturn(intervalInSec);
|
||||
when(leapArray.getIntervalInSecond()).thenReturn((double) intervalInSec);
|
||||
|
||||
final ParamMapBucket b1 = generateBucket(a1, paramPrefix);
|
||||
final ParamMapBucket b2 = generateBucket(a2, paramPrefix);
|
||||
|
|
@ -122,8 +122,8 @@ public class HotParameterLeapArrayTest {
|
|||
public void testGetRollingAvg() {
|
||||
HotParameterLeapArray leapArray = mock(HotParameterLeapArray.class);
|
||||
when(leapArray.getRollingSum(any(RollingParamEvent.class), any(Object.class))).thenReturn(15L);
|
||||
when(leapArray.getIntervalInSec()).thenReturn(1)
|
||||
.thenReturn(2);
|
||||
when(leapArray.getIntervalInSecond()).thenReturn(1d)
|
||||
.thenReturn(2d);
|
||||
when(leapArray.getRollingAvg(any(RollingParamEvent.class), any(Object.class))).thenCallRealMethod();
|
||||
|
||||
assertEquals(15.0d, leapArray.getRollingAvg(RollingParamEvent.REQUEST_PASSED, "abc"), 0.001);
|
||||
|
|
|
|||
Loading…
Reference in New Issue