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 06739154..0927d8e4 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 @@ -15,11 +15,13 @@ */ package com.alibaba.csp.sentinel.node; +import java.util.List; import java.util.Map; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.node.metric.MetricNode; import com.alibaba.csp.sentinel.slots.statistic.metric.DebugSupport; +import com.alibaba.csp.sentinel.util.function.Predicate; /** * Holds real-time statistics for resources. @@ -146,6 +148,15 @@ public interface Node extends OccupySupport, DebugSupport { */ Map metrics(); + /** + * Fetch all raw metric items that satisfies the time predicate. + * + * @param timePredicate time predicate + * @return raw metric items that satisfies the time predicate + * @since 1.7.0 + */ + List rawMetricsInMin(Predicate timePredicate); + /** * Add pass 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 80a8443c..14df68bb 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 @@ -20,6 +20,7 @@ import com.alibaba.csp.sentinel.slots.statistic.base.LongAdder; import com.alibaba.csp.sentinel.slots.statistic.metric.ArrayMetric; import com.alibaba.csp.sentinel.slots.statistic.metric.Metric; import com.alibaba.csp.sentinel.util.TimeUtil; +import com.alibaba.csp.sentinel.util.function.Predicate; import java.util.List; import java.util.Map; @@ -131,6 +132,11 @@ public class StatisticNode implements Node { return metrics; } + @Override + public List rawMetricsInMin(Predicate timePredicate) { + return rollingCounterInMinute.detailsOnCondition(timePredicate); + } + private boolean isNodeInTime(MetricNode node, long currentTime) { return node.getTimestamp() > lastFetchTime && node.getTimestamp() < currentTime; } 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 8647d994..521414a8 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 @@ -25,6 +25,7 @@ import com.alibaba.csp.sentinel.slots.statistic.base.LeapArray; import com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket; import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap; import com.alibaba.csp.sentinel.slots.statistic.metric.occupy.OccupiableBucketLeapArray; +import com.alibaba.csp.sentinel.util.function.Predicate; /** * The basic metric class in Sentinel using a {@link BucketLeapArray} internal. @@ -153,33 +154,56 @@ public class ArrayMetric implements Metric { @Override public List details() { - List details = new ArrayList(); + List details = new ArrayList<>(); data.currentWindow(); List> list = data.list(); for (WindowWrap window : list) { if (window == null) { continue; } - MetricNode node = new MetricNode(); - node.setBlockQps(window.value().block()); - node.setExceptionQps(window.value().exception()); - node.setPassQps(window.value().pass()); - long successQps = window.value().success(); - node.setSuccessQps(successQps); - if (successQps != 0) { - node.setRt(window.value().rt() / successQps); - } else { - node.setRt(window.value().rt()); + + details.add(fromBucket(window)); + } + + return details; + } + + @Override + public List detailsOnCondition(Predicate timePredicate) { + List details = new ArrayList<>(); + data.currentWindow(); + List> list = data.list(); + for (WindowWrap window : list) { + if (window == null) { + continue; + } + if (timePredicate != null && !timePredicate.test(window.windowStart())) { + continue; } - node.setTimestamp(window.windowStart()); - node.setOccupiedPassQps(window.value().occupiedPass()); - details.add(node); + details.add(fromBucket(window)); } return details; } + private MetricNode fromBucket(WindowWrap wrap) { + MetricNode node = new MetricNode(); + node.setBlockQps(wrap.value().block()); + node.setExceptionQps(wrap.value().exception()); + node.setPassQps(wrap.value().pass()); + long successQps = wrap.value().success(); + node.setSuccessQps(successQps); + if (successQps != 0) { + node.setRt(wrap.value().rt() / successQps); + } else { + node.setRt(wrap.value().rt()); + } + node.setTimestamp(wrap.windowStart()); + node.setOccupiedPassQps(wrap.value().occupiedPass()); + return node; + } + @Override public MetricBucket[] windows() { data.currentWindow(); diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java index f6a73f5f..b79cd881 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java @@ -19,6 +19,7 @@ import java.util.List; import com.alibaba.csp.sentinel.node.metric.MetricNode; import com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket; +import com.alibaba.csp.sentinel.util.function.Predicate; /** * Represents a basic structure recording invocation metrics of protected resources. @@ -84,6 +85,15 @@ public interface Metric extends DebugSupport { */ List details(); + /** + * Generate aggregated metric items that satisfies the time predicate. + * + * @param timePredicate time predicate + * @return aggregated metric items + * @since 1.7.0 + */ + List detailsOnCondition(Predicate timePredicate); + /** * Get the raw window array. * diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetricTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetricTest.java index 80293d48..2097670e 100644 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetricTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetricTest.java @@ -16,9 +16,14 @@ package com.alibaba.csp.sentinel.slots.statistic.metric; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import com.alibaba.csp.sentinel.node.metric.MetricNode; +import com.alibaba.csp.sentinel.slots.statistic.MetricEvent; import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap; import com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket; +import com.alibaba.csp.sentinel.util.function.Predicate; import org.junit.Test; @@ -38,7 +43,8 @@ public class ArrayMetricTest { @Test public void testOperateArrayMetric() { BucketLeapArray leapArray = mock(BucketLeapArray.class); - final WindowWrap windowWrap = new WindowWrap(windowLengthInMs, 0, new MetricBucket()); + final WindowWrap windowWrap = new WindowWrap(windowLengthInMs, 0, + new MetricBucket()); when(leapArray.currentWindow()).thenReturn(windowWrap); when(leapArray.values()).thenReturn(new ArrayList() {{ add(windowWrap.value()); }}); @@ -70,4 +76,45 @@ public class ArrayMetricTest { assertEquals(expectedException, metric.exception()); assertEquals(expectedRt, metric.rt()); } -} \ No newline at end of file + + @Test + public void testGetMetricDetailsOnCondition() { + BucketLeapArray leapArray = mock(BucketLeapArray.class); + // Mock interval=2s, sampleCount=2 + final WindowWrap w1 = new WindowWrap<>(windowLengthInMs, 500, + new MetricBucket().add(MetricEvent.PASS, 1)); + final WindowWrap w2 = new WindowWrap<>(windowLengthInMs, 1000, + new MetricBucket().add(MetricEvent.PASS, 2)); + final WindowWrap w3 = new WindowWrap<>(windowLengthInMs, 1500, + new MetricBucket().add(MetricEvent.PASS, 3)); + final WindowWrap w4 = new WindowWrap<>(windowLengthInMs, 2000, + new MetricBucket().add(MetricEvent.PASS, 4)); + List> buckets = Arrays.asList(w1, w2, w3, w4); + when(leapArray.currentWindow()).thenReturn(w4); + when(leapArray.list()).thenReturn(buckets); + + ArrayMetric metric = new ArrayMetric(leapArray); + + // Empty condition -> retrieve all + assertEquals(4, metric.detailsOnCondition(null).size()); + // Normal condition + List metricNodes = metric.detailsOnCondition(new Predicate() { + @Override + public boolean test(Long t) { + return t >= 1500; + } + }); + assertEquals(2, metricNodes.size()); + assertEquals(3, metricNodes.get(0).getPassQps()); + assertEquals(4, metricNodes.get(1).getPassQps()); + + // Future condition + metricNodes = metric.detailsOnCondition(new Predicate() { + @Override + public boolean test(Long t) { + return t >= 2500; + } + }); + assertEquals(0, metricNodes.size()); + } +}