diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/IntervalProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/IntervalProperty.java
index 12c85cd0..ad877822 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/IntervalProperty.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/IntervalProperty.java
@@ -16,8 +16,8 @@
package com.alibaba.csp.sentinel.node;
import com.alibaba.csp.sentinel.log.RecordLog;
-import com.alibaba.csp.sentinel.property.PropertyListener;
import com.alibaba.csp.sentinel.property.SentinelProperty;
+import com.alibaba.csp.sentinel.property.SimplePropertyListener;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
/***
@@ -25,36 +25,42 @@ import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
*
* @author youji.zj
* @author jialiang.linjl
+ * @author CarpenterLee
*/
public class IntervalProperty {
+ /**
+ *
+ * Interval in seconds. This variable determines sensitivity of the QPS calculation.
+ *
+ * DO NOT MODIFY this value directly, use {@link #updateInterval(int)}, otherwise the modification will not
+ * take effect.
+ */
public static volatile int INTERVAL = 1;
- public static void init(SentinelProperty dataSource) {
- dataSource.addListener(new FlowIntervalPropertyListener());
- }
-
- private static class FlowIntervalPropertyListener implements PropertyListener {
- @Override
- public void configUpdate(Integer value) {
- if (value == null) {
- value = 1;
+ public static void register2Property(SentinelProperty property) {
+ property.addListener(new SimplePropertyListener() {
+ @Override
+ public void configUpdate(Integer value) {
+ if (value != null) {
+ updateInterval(value);
+ }
}
- INTERVAL = value;
- RecordLog.info("Init flow interval: " + INTERVAL);
- }
+ });
+ }
- @Override
- public void configLoad(Integer value) {
- if (value == null) {
- value = 1;
- }
- INTERVAL = value;
- for (ClusterNode node : ClusterBuilderSlot.getClusterNodeMap().values()) {
- node.reset();
- }
- RecordLog.info("Flow interval change received: " + INTERVAL);
+ /**
+ * Update the {@link #INTERVAL}, All {@link ClusterNode}s will be reset if newInterval is
+ * different from {@link #INTERVAL}
+ *
+ * @param newInterval New interval to set.
+ */
+ public static void updateInterval(int newInterval) {
+ if (newInterval != INTERVAL) {
+ INTERVAL = newInterval;
+ ClusterBuilderSlot.resetClusterNodes();
}
+ RecordLog.info("INTERVAL updated to: " + INTERVAL);
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/Node.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/Node.java
index 427c45a7..ecd7b018 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/Node.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/Node.java
@@ -119,7 +119,8 @@ public interface Node {
void decreaseThreadNum();
/**
- * Reset the internal counter.
+ * Reset the internal counter. Reset is needed when {@link IntervalProperty#INTERVAL} or
+ * {@link SampleCountProperty#SAMPLE_COUNT} is changed.
*/
void reset();
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/SampleCountProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/SampleCountProperty.java
index effc7ac4..8001a80e 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/SampleCountProperty.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/SampleCountProperty.java
@@ -21,31 +21,45 @@ import com.alibaba.csp.sentinel.property.SimplePropertyListener;
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
/**
+ * Holds statistic buckets count per second.
+ *
* @author jialiang.linjl
+ * @author CarpenterLee
*/
public class SampleCountProperty {
- public static volatile int sampleCount = 2;
-
- public static void init(SentinelProperty property) {
+ /**
+ *
+ * Statistic buckets count per second. This variable determines sensitivity of the QPS calculation.
+ * DO NOT MODIFY this value directly, use {@link #updateSampleCount(int)}, otherwise the modification will not
+ * take effect.
+ *
+ * Node that this value must be divisor of 1000.
+ */
+ public static volatile int SAMPLE_COUNT = 2;
- try {
- property.addListener(new SimplePropertyListener() {
- @Override
- public void configUpdate(Integer value) {
- if (value != null) {
- sampleCount = value;
- // Reset the value.
- for (ClusterNode node : ClusterBuilderSlot.getClusterNodeMap().values()) {
- node.reset();
- }
- }
- RecordLog.info("Current SampleCount: " + sampleCount);
+ public static void register2Property(SentinelProperty property) {
+ property.addListener(new SimplePropertyListener() {
+ @Override
+ public void configUpdate(Integer value) {
+ if (value != null) {
+ updateSampleCount(value);
}
+ }
+ });
+ }
- });
- } catch (Exception e) {
- RecordLog.info(e.getMessage(), e);
+ /**
+ * Update the {@link #SAMPLE_COUNT}. All {@link ClusterNode}s will be reset if newSampleCount
+ * is different from {@link #SAMPLE_COUNT}.
+ *
+ * @param newSampleCount New sample count to set. This value must be divisor of 1000.
+ */
+ public static void updateSampleCount(int newSampleCount) {
+ if (newSampleCount != SAMPLE_COUNT) {
+ SAMPLE_COUNT = newSampleCount;
+ ClusterBuilderSlot.resetClusterNodes();
}
+ RecordLog.info("SAMPLE_COUNT updated to: " + SAMPLE_COUNT);
}
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java
index a3e5418b..4158262b 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java
@@ -31,7 +31,7 @@ import com.alibaba.csp.sentinel.slots.statistic.metric.Metric;
*/
public class StatisticNode implements Node {
- private transient Metric rollingCounterInSecond = new ArrayMetric(1000 / SampleCountProperty.sampleCount,
+ private transient volatile Metric rollingCounterInSecond = new ArrayMetric(1000 / SampleCountProperty.SAMPLE_COUNT,
IntervalProperty.INTERVAL);
/**
@@ -70,7 +70,7 @@ public class StatisticNode implements Node {
@Override
public void reset() {
- rollingCounterInSecond = new ArrayMetric(1000 / SampleCountProperty.sampleCount, IntervalProperty.INTERVAL);
+ rollingCounterInSecond = new ArrayMetric(1000 / SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL);
}
@Override
@@ -131,7 +131,7 @@ public class StatisticNode implements Node {
@Override
public long maxSuccessQps() {
- return rollingCounterInSecond.maxSuccess() * SampleCountProperty.sampleCount;
+ return rollingCounterInSecond.maxSuccess() * SampleCountProperty.SAMPLE_COUNT;
}
@Override
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/metric/MetricTimerListener.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/metric/MetricTimerListener.java
index 9222a7b6..763bb951 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/metric/MetricTimerListener.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/metric/MetricTimerListener.java
@@ -36,7 +36,6 @@ public class MetricTimerListener implements Runnable {
public void run() {
Map> maps = new TreeMap>();
- // 每5秒打印一次,把丢弃的seconds都给丢掉。
for (Entry e : ClusterBuilderSlot.getClusterNodeMap().entrySet()) {
String name = e.getKey().getName();
ClusterNode node = e.getValue();
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java
index 92940b46..9b851cfd 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java
@@ -24,7 +24,9 @@ import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.node.ClusterNode;
import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.node.IntervalProperty;
import com.alibaba.csp.sentinel.node.Node;
+import com.alibaba.csp.sentinel.node.SampleCountProperty;
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
@@ -150,4 +152,13 @@ public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot
return clusterNodeMap;
}
+ /**
+ * Reset all {@link ClusterNode}s. Reset is needed when {@link IntervalProperty#INTERVAL} or
+ * {@link SampleCountProperty#SAMPLE_COUNT} is changed.
+ */
+ public static void resetClusterNodes() {
+ for (ClusterNode node : clusterNodeMap.values()) {
+ node.reset();
+ }
+ }
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java
index f3469161..30761e2f 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java
@@ -23,15 +23,23 @@ import java.util.concurrent.locks.ReentrantLock;
import com.alibaba.csp.sentinel.util.TimeUtil;
/**
+ *
* Basic data structure for statistic metrics.
+ *
+ *
+ * Using sliding window algorithm to count data. Each bucket cover {@link #windowLengthInMs} time span,
+ * and the total time span is {@link #intervalInMs}, so the total bucket count is:
+ * {@link #sampleCount} = intervalInMs / windowLengthInMs.
+ *
*
- * @param type of data wrapper
+ * @param type of data bucket.
* @author jialiang.linjl
* @author Eric Zhao
+ * @author CarpenterLee
*/
public abstract class LeapArray {
- protected int windowLength;
+ protected int windowLengthInMs;
protected int sampleCount;
protected int intervalInMs;
@@ -39,10 +47,15 @@ public abstract class LeapArray {
private final ReentrantLock updateLock = new ReentrantLock();
- public LeapArray(int windowLength, int intervalInSec) {
- this.windowLength = windowLength;
+ /**
+ * The total bucket count is: {@link #sampleCount} = intervalInSec * 1000 / windowLengthInMs.
+ * @param windowLengthInMs a single window bucket's time length in milliseconds.
+ * @param intervalInSec the total time span of this {@link LeapArray} in seconds.
+ */
+ public LeapArray(int windowLengthInMs, int intervalInSec) {
+ this.windowLengthInMs = windowLengthInMs;
this.intervalInMs = intervalInSec * 1000;
- this.sampleCount = intervalInMs / windowLength;
+ this.sampleCount = intervalInMs / windowLengthInMs;
this.array = new AtomicReferenceArray>(sampleCount);
}
@@ -79,17 +92,17 @@ public abstract class LeapArray {
* @return the window at provided timestamp
*/
public WindowWrap currentWindow(long time) {
- long timeId = time / windowLength;
+ long timeId = time / windowLengthInMs;
// Calculate current index.
int idx = (int)(timeId % array.length());
// Cut the time to current window start.
- time = time - time % windowLength;
+ time = time - time % windowLengthInMs;
while (true) {
WindowWrap old = array.get(idx);
if (old == null) {
- WindowWrap window = new WindowWrap(windowLength, time, newEmptyBucket());
+ WindowWrap window = new WindowWrap(windowLengthInMs, time, newEmptyBucket());
if (array.compareAndSet(idx, null, window)) {
return window;
} else {
@@ -111,22 +124,22 @@ public abstract class LeapArray {
} else if (time < old.windowStart()) {
// Cannot go through here.
- return new WindowWrap(windowLength, time, newEmptyBucket());
+ return new WindowWrap(windowLengthInMs, time, newEmptyBucket());
}
}
}
public WindowWrap getPreviousWindow(long time) {
- long timeId = (time - windowLength) / windowLength;
+ long timeId = (time - windowLengthInMs) / windowLengthInMs;
int idx = (int)(timeId % array.length());
- time = time - windowLength;
+ time = time - windowLengthInMs;
WindowWrap wrap = array.get(idx);
if (wrap == null || isWindowDeprecated(wrap)) {
return null;
}
- if (wrap.windowStart() + windowLength < (time)) {
+ if (wrap.windowStart() + windowLengthInMs < (time)) {
return null;
}
@@ -138,7 +151,7 @@ public abstract class LeapArray {
}
public T getWindowValue(long time) {
- long timeId = time / windowLength;
+ long timeId = time / windowLengthInMs;
int idx = (int)(timeId % array.length());
WindowWrap old = array.get(idx);
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java
index e8529669..5818e4ba 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java
@@ -25,9 +25,9 @@ package com.alibaba.csp.sentinel.slots.statistic.base;
public class WindowWrap {
/**
- * The length of the window.
+ * a single window bucket's time length in milliseconds.
*/
- private final long windowLength;
+ private final long windowLengthInMs;
/**
* Start time of the window in milliseconds.
@@ -40,18 +40,18 @@ public class WindowWrap {
private T value;
/**
- * @param windowLength the time length of the window
+ * @param windowLengthInMs a single window bucket's time length in milliseconds.
* @param windowStart the start timestamp of the window
* @param value window data
*/
- public WindowWrap(long windowLength, long windowStart, T value) {
- this.windowLength = windowLength;
+ public WindowWrap(long windowLengthInMs, long windowStart, T value) {
+ this.windowLengthInMs = windowLengthInMs;
this.windowStart = windowStart;
this.value = value;
}
public long windowLength() {
- return windowLength;
+ return windowLengthInMs;
}
public long windowStart() {
@@ -74,7 +74,7 @@ public class WindowWrap {
@Override
public String toString() {
return "WindowWrap{" +
- "windowLength=" + windowLength +
+ "windowLengthInMs=" + windowLengthInMs +
", windowStart=" + windowStart +
", value=" + value +
'}';
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java
index b7df1efa..684c8365 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java
@@ -33,8 +33,14 @@ public class ArrayMetric implements Metric {
private final MetricsLeapArray data;
- public ArrayMetric(int windowLength, int interval) {
- this.data = new MetricsLeapArray(windowLength, interval);
+ /**
+ * Constructor
+ *
+ * @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);
}
/**
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/MetricsLeapArray.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/MetricsLeapArray.java
index 0c2dcb86..d5972b6d 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/MetricsLeapArray.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/MetricsLeapArray.java
@@ -22,11 +22,18 @@ import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
/**
* The fundamental data structure for metric statistics in a time window.
*
+ * @see LeapArray
* @author jialiang.linjl
* @author Eric Zhao
*/
public class MetricsLeapArray extends LeapArray {
+ /**
+ * Constructor
+ *
+ * @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);
}