瀏覽代碼

Code and javadoc refinement

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
master
Eric Zhao 6 年之前
父節點
當前提交
f8bc6f631a
共有 11 個檔案被更改,包括 247 行新增44 行删除
  1. +64
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java
  2. +130
    -26
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java
  3. +3
    -3
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/MetricBucket.java
  4. +11
    -5
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java
  5. +0
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java
  6. +11
    -1
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java
  7. +2
    -3
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/MetricsLeapArray.java
  8. +1
    -1
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/cache/CacheMap.java
  9. +6
    -2
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/cache/ConcurrentLinkedHashMapWrapper.java
  10. +5
    -1
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/data/ParamMapBucket.java
  11. +14
    -0
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/HotParameterLeapArray.java

+ 64
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java 查看文件

@@ -26,11 +26,72 @@ import com.alibaba.csp.sentinel.slots.statistic.metric.ArrayMetric;
import com.alibaba.csp.sentinel.slots.statistic.metric.Metric;

/**
* <p>The statistic node keep three kinds of real-time statistics metrics:</p>
* <ol>
* <li>metrics in second level ({@code rollingCounterInSecond})</li>
* <li>metrics in minute level ({@code rollingCounterInMinute})</li>
* <li>thread count</li>
* </ol>
*
* <p>
* Sentinel use sliding window to record and count the resource statistics in real-time.
* The sliding window infrastructure behind the {@link ArrayMetric} is {@code LeapArray}.
* </p>
*
* <p>
* case 1: When the first request comes in, Sentinel will create a new window bucket of
* a specified time-span to store running statics, such as total response time(rt),
* incoming request(QPS), block request(bq), etc. And the time-span is defined by sample count.
* </p>
* <pre>
* 0 100ms
* +-------+--→ Sliding Windows
* ^
* |
* request
* </pre>
* <p>
* Sentinel use the statics of the valid buckets to decide whether this request can be passed.
* For example, if a rule defines that only 100 requests can be passed,
* it will sum all qps in valid buckets, and compare it to the threshold defined in rule.
* </p>
*
* <p>case 2: continuous requests</p>
* <pre>
* 0 100ms 200ms 300ms
* +-------+-------+-------+-----→ Sliding Windows
* ^
* |
* request
* </pre>
*
* <p>case 3: requests keeps coming, and previous buckets become invalid</p>
* <pre>
* 0 100ms 200ms 800ms 900ms 1000ms 1300ms
* +-------+-------+ ...... +-------+-------+ ...... +-------+-----→ Sliding Windows
* ^
* |
* request
* </pre>
*
* <p>The sliding window should become:</p>
* <pre>
* 300ms 800ms 900ms 1000ms 1300ms
* + ...... +-------+ ...... +-------+-----→ Sliding Windows
* ^
* |
* request
* </pre>
*
* @author qinan.qn
* @author jialiang.linjl
*/
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,
IntervalProperty.INTERVAL);

@@ -40,6 +101,9 @@ public class StatisticNode implements Node {
*/
private transient Metric rollingCounterInMinute = new ArrayMetric(1000, 60);

/**
* The counter for thread count.
*/
private AtomicInteger curThreadNum = new AtomicInteger(0);

private long lastFetchTime = -1;


+ 130
- 26
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java 查看文件

@@ -32,10 +32,10 @@ import com.alibaba.csp.sentinel.util.TimeUtil;
* {@link #sampleCount} = intervalInMs / windowLengthInMs.
* </p>
*
* @param <T> type of data bucket.
* @param <T> type of statistic data
* @author jialiang.linjl
* @author Eric Zhao
* @author CarpenterLee
* @author Carpenter Lee
*/
public abstract class LeapArray<T> {

@@ -48,7 +48,8 @@ public abstract class LeapArray<T> {
private final ReentrantLock updateLock = new ReentrantLock();

/**
* The total bucket count is: {@link #sampleCount} = intervalInSec * 1000 / windowLengthInMs.
* 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.
*/
@@ -61,74 +62,129 @@ public abstract class LeapArray<T> {
}

/**
* Get the window at current timestamp.
* Get the bucket at current timestamp.
*
* @return the window at current timestamp
* @return the bucket at current timestamp
*/
public WindowWrap<T> currentWindow() {
return currentWindow(TimeUtil.currentTimeMillis());
}

/**
* Create a new bucket.
* Create a new statistic value for bucket.
*
* @return the new empty bucket
*/
public abstract T newEmptyBucket();

/**
* Reset current window to provided start time and reset all counters.
* Reset given bucket to provided start time and reset the value.
*
* @param startTime the start time of the window
* @param windowWrap current window
* @return new clean window wrap
* @param startTime the start time of the bucket
* @param windowWrap current bucket
* @return new clean bucket at given start time
*/
protected abstract WindowWrap<T> resetWindowTo(WindowWrap<T> windowWrap, long startTime);

/**
* Get window at provided timestamp.
* Get bucket item at provided timestamp.
*
* @param time a valid timestamp
* @return the window at provided timestamp
* @return current bucket item at provided timestamp
*/
public WindowWrap<T> currentWindow(long time) {
long timeId = time / windowLengthInMs;
// Calculate current index.
// Calculate current index so we can map the timestamp to the leap array.
int idx = (int)(timeId % array.length());

// Cut the time to current window start.
time = time - time % windowLengthInMs;
// Calculate current bucket start time.
long windowStart = time - time % windowLengthInMs;

/*
* Get bucket item at given time from the array.
*
* (1) Bucket is absent, then just create a new bucket and CAS update to circular array.
* (2) Bucket is up-to-date, then just return the bucket.
* (3) Bucket is deprecated, then reset current bucket and clean all deprecated buckets.
*/
while (true) {
WindowWrap<T> old = array.get(idx);
if (old == null) {
WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, time, newEmptyBucket());
/*
* B0 B1 B2 NULL B4
* ||_______|_______|_______|_______|_______||___
* 200 400 600 800 1000 1200 timestamp
* ^
* time=888
* bucket is empty, so create new and update
*
* If the old bucket is absent, then we create a new bucket at {@code windowStart},
* then try to update circular array via a CAS operation. Only one thread can
* succeed to update, while other threads yield its time slice.
*/
WindowWrap<T> window = new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket());
if (array.compareAndSet(idx, null, window)) {
// Successfully updated, return the created bucket.
return window;
} else {
// Contention failed, the thread will yield its time slice to wait for bucket available.
Thread.yield();
}
} else if (time == old.windowStart()) {
} else if (windowStart == old.windowStart()) {
/*
* B0 B1 B2 B3 B4
* ||_______|_______|_______|_______|_______||___
* 200 400 600 800 1000 1200 timestamp
* ^
* time=888
* startTime of Bucket 3: 800, so it's up-to-date
*
* If current {@code windowStart} is equal to the start timestamp of old bucket,
* that means the time is within the bucket, so directly return the bucket.
*/
return old;
} else if (time > old.windowStart()) {
} else if (windowStart > old.windowStart()) {
/*
* (old)
* B0 B1 B2 NULL B4
* |_______||_______|_______|_______|_______|_______||___
* ... 1200 1400 1600 1800 2000 2200 timestamp
* ^
* time=1676
* startTime of Bucket 2: 400, deprecated, should be reset
*
* If the start timestamp of old bucket is behind provided time, that means
* the bucket is deprecated. We have to reset the bucket to current {@code windowStart}.
* Note that the reset and clean-up operations are hard to be atomic,
* so we need a update lock to guarantee the correctness of bucket update.
*
* The update lock is fine-grained and will take effect only when
* bucket is deprecated, so in most cases it won't lead to performance loss.
*/
if (updateLock.tryLock()) {
try {
// if (old is deprecated) then [LOCK] resetTo currentTime.
return resetWindowTo(old, time);
// Successfully get the update lock, now we reset the bucket.
return resetWindowTo(old, windowStart);
} finally {
updateLock.unlock();
}
} else {
// Contention failed, the thread will yield its time slice to wait for bucket available.
Thread.yield();
}

} else if (time < old.windowStart()) {
// Cannot go through here.
return new WindowWrap<T>(windowLengthInMs, time, newEmptyBucket());
} else if (windowStart < old.windowStart()) {
// Should not go through here, as the provided time is already behind.
return new WindowWrap<T>(windowLengthInMs, windowStart, newEmptyBucket());
}
}
}

/**
* Get the previous bucket item before provided timestamp.
*
* @param time a valid timestamp
* @return the previous bucket item before provided timestamp
*/
public WindowWrap<T> getPreviousWindow(long time) {
long timeId = (time - windowLengthInMs) / windowLengthInMs;
int idx = (int)(timeId % array.length());
@@ -146,10 +202,21 @@ public abstract class LeapArray<T> {
return wrap;
}

/**
* Get the previous bucket item for current timestamp.
*
* @return the previous bucket item for current timestamp
*/
public WindowWrap<T> getPreviousWindow() {
return getPreviousWindow(System.currentTimeMillis());
return getPreviousWindow(TimeUtil.currentTimeMillis());
}

/**
* Get statistic value from bucket for provided timestamp.
*
* @param time a valid timestamp
* @return the statistic value if bucket for provided timestamp is up-to-date; otherwise null
*/
public T getWindowValue(long time) {
long timeId = time / windowLengthInMs;
int idx = (int)(timeId % array.length());
@@ -162,10 +229,23 @@ public abstract class LeapArray<T> {
return old.value();
}

private boolean isWindowDeprecated(WindowWrap<T> windowWrap) {
/**
* Check if a bucket is deprecated, which means that the bucket
* has been behind for at least an entire window time span.
*
* @param windowWrap a non-null bucket
* @return true if the bucket is deprecated; otherwise false
*/
private boolean isWindowDeprecated(/*@NonNull*/ WindowWrap<T> windowWrap) {
return TimeUtil.currentTimeMillis() - windowWrap.windowStart() >= intervalInMs;
}

/**
* Get valid bucket list for entire sliding window.
* The list will only contain "valid" buckets.
*
* @return valid bucket list for entire sliding window.
*/
public List<WindowWrap<T>> list() {
List<WindowWrap<T>> result = new ArrayList<WindowWrap<T>>();

@@ -180,6 +260,12 @@ public abstract class LeapArray<T> {
return result;
}

/**
* Get aggregated value list for entire sliding window.
* The list will only contain value from "valid" buckets.
*
* @return aggregated value list for entire sliding window
*/
public List<T> values() {
List<T> result = new ArrayList<T>();

@@ -192,4 +278,22 @@ public abstract class LeapArray<T> {
}
return result;
}

/**
* Get sample count (total amount of buckets).
*
* @return sample count
*/
public int getSampleCount() {
return sampleCount;
}

/**
* Get total interval length of the sliding window.
*
* @return interval in second
*/
public int getIntervalInSecond() {
return intervalInMs / 1000;
}
}

+ 3
- 3
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/MetricBucket.java 查看文件

@@ -18,7 +18,7 @@ package com.alibaba.csp.sentinel.slots.statistic.base;
import com.alibaba.csp.sentinel.Constants;

/**
* Represents metrics data in a period of time window.
* Represents metrics data in a period of time span.
*
* @author jialiang.linjl
* @author Eric Zhao
@@ -42,9 +42,9 @@ public class MetricBucket {
}

/**
* Clean the adders and reset window to provided start time.
* Reset the adders.
*
* @return new clean window
* @return new metric bucket in initial state
*/
public MetricBucket reset() {
pass.reset();


+ 11
- 5
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java 查看文件

@@ -25,24 +25,24 @@ package com.alibaba.csp.sentinel.slots.statistic.base;
public class WindowWrap<T> {

/**
* a single window bucket's time length in milliseconds.
* Time length of a single window bucket in milliseconds.
*/
private final long windowLengthInMs;

/**
* Start time of the window in milliseconds.
* Start timestamp of the window in milliseconds.
*/
private long windowStart;

/**
* Statistic value.
* Statistic data.
*/
private T value;

/**
* @param windowLengthInMs a single window bucket's time length in milliseconds.
* @param windowStart the start timestamp of the window
* @param value window data
* @param windowStart the start timestamp of the window
* @param value statistic data
*/
public WindowWrap(long windowLengthInMs, long windowStart, T value) {
this.windowLengthInMs = windowLengthInMs;
@@ -66,6 +66,12 @@ public class WindowWrap<T> {
this.value = value;
}

/**
* Reset start timestamp of current bucket to provided time.
*
* @param startTime valid start timestamp
* @return bucket after reset
*/
public WindowWrap<T> resetTo(long startTime) {
this.windowStart = startTime;
return this;


+ 0
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java 查看文件

@@ -34,8 +34,6 @@ public class ArrayMetric implements Metric {
private final MetricsLeapArray data;

/**
* Constructor
*
* @param windowLengthInMs a single window bucket's time length in milliseconds.
* @param intervalInSec the total time span of this {@link ArrayMetric} in seconds.
*/


+ 11
- 1
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java 查看文件

@@ -35,6 +35,11 @@ public interface Metric {
*/
long success();

/**
* Get max success count.
*
* @return max success count
*/
long maxSuccess();

/**
@@ -59,7 +64,7 @@ public interface Metric {
long pass();

/**
* Get total RT.
* Get total response time.
*
* @return total RT
*/
@@ -72,6 +77,11 @@ public interface Metric {
*/
long minRt();

/**
* Get aggregated metric nodes of all resources.
*
* @return metric node list of all resources
*/
List<MetricNode> details();

/**


+ 2
- 3
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/MetricsLeapArray.java 查看文件

@@ -20,7 +20,7 @@ import com.alibaba.csp.sentinel.slots.statistic.base.MetricBucket;
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;

/**
* The fundamental data structure for metric statistics in a time window.
* The fundamental data structure for metric statistics in a time span.
*
* @see LeapArray
* @author jialiang.linjl
@@ -29,8 +29,6 @@ import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
public class MetricsLeapArray extends LeapArray<MetricBucket> {

/**
* Constructor
*
* @param windowLengthInMs a single window bucket's time length in milliseconds.
* @param intervalInSec the total time span of this {@link MetricsLeapArray} in seconds.
*/
@@ -45,6 +43,7 @@ public class MetricsLeapArray extends LeapArray<MetricBucket> {

@Override
protected WindowWrap<MetricBucket> resetWindowTo(WindowWrap<MetricBucket> w, long startTime) {
// Update the start time and reset value.
w.resetTo(startTime);
w.value().reset();
return w;


+ 1
- 1
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/cache/CacheMap.java 查看文件

@@ -41,5 +41,5 @@ public interface CacheMap<K, V> {

void clear();

Set<K> ascendingKeySet();
Set<K> keySet(boolean ascending);
}

+ 6
- 2
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/cache/ConcurrentLinkedHashMapWrapper.java 查看文件

@@ -86,7 +86,11 @@ public class ConcurrentLinkedHashMapWrapper<T, R> implements CacheMap<T, R> {
}

@Override
public Set<T> ascendingKeySet() {
return map.ascendingKeySet();
public Set<T> keySet(boolean ascending) {
if (ascending) {
return map.ascendingKeySet();
} else {
return map.descendingKeySet();
}
}
}

+ 5
- 1
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/data/ParamMapBucket.java 查看文件

@@ -66,7 +66,11 @@ public class ParamMapBucket {
}

public Set<Object> ascendingKeySet(RollingParamEvent type) {
return data[type.ordinal()].ascendingKeySet();
return data[type.ordinal()].keySet(true);
}

public Set<Object> descendingKeySet(RollingParamEvent type) {
return data[type.ordinal()].keySet(false);
}

public static final int DEFAULT_MAX_CAPACITY = 200;


+ 14
- 0
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/HotParameterLeapArray.java 查看文件

@@ -60,10 +60,24 @@ public class HotParameterLeapArray extends LeapArray<ParamMapBucket> {
return w;
}

/**
* Add event count for specific parameter value.
*
* @param event target event
* @param count count to add
* @param value parameter value
*/
public void addValue(RollingParamEvent event, int count, Object value) {
currentWindow().value().add(event, count, value);
}

/**
* Get "top-N" value-QPS map of provided event.
*
* @param event target event
* @param number max number of values
* @return "top-N" value map
*/
public Map<Object, Double> getTopValues(RollingParamEvent event, int number) {
currentWindow();
List<ParamMapBucket> buckets = this.values();


Loading…
取消
儲存