* Add detailsOnCondition method in Metric interface to filter MetricNode within the time condition. * Add rawMetricsInMin method in Node interface, which will retrieve and generate metric items from the min-level sliding window on condition. * Add test cases for detailsOnCondition. Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -15,11 +15,13 @@ | |||||
*/ | */ | ||||
package com.alibaba.csp.sentinel.node; | package com.alibaba.csp.sentinel.node; | ||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import com.alibaba.csp.sentinel.Entry; | import com.alibaba.csp.sentinel.Entry; | ||||
import com.alibaba.csp.sentinel.node.metric.MetricNode; | import com.alibaba.csp.sentinel.node.metric.MetricNode; | ||||
import com.alibaba.csp.sentinel.slots.statistic.metric.DebugSupport; | import com.alibaba.csp.sentinel.slots.statistic.metric.DebugSupport; | ||||
import com.alibaba.csp.sentinel.util.function.Predicate; | |||||
/** | /** | ||||
* Holds real-time statistics for resources. | * Holds real-time statistics for resources. | ||||
@@ -146,6 +148,15 @@ public interface Node extends OccupySupport, DebugSupport { | |||||
*/ | */ | ||||
Map<Long, MetricNode> metrics(); | Map<Long, MetricNode> 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<MetricNode> rawMetricsInMin(Predicate<Long> timePredicate); | |||||
/** | /** | ||||
* Add pass count. | * Add pass count. | ||||
* | * | ||||
@@ -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.ArrayMetric; | ||||
import com.alibaba.csp.sentinel.slots.statistic.metric.Metric; | import com.alibaba.csp.sentinel.slots.statistic.metric.Metric; | ||||
import com.alibaba.csp.sentinel.util.TimeUtil; | import com.alibaba.csp.sentinel.util.TimeUtil; | ||||
import com.alibaba.csp.sentinel.util.function.Predicate; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
@@ -131,6 +132,11 @@ public class StatisticNode implements Node { | |||||
return metrics; | return metrics; | ||||
} | } | ||||
@Override | |||||
public List<MetricNode> rawMetricsInMin(Predicate<Long> timePredicate) { | |||||
return rollingCounterInMinute.detailsOnCondition(timePredicate); | |||||
} | |||||
private boolean isNodeInTime(MetricNode node, long currentTime) { | private boolean isNodeInTime(MetricNode node, long currentTime) { | ||||
return node.getTimestamp() > lastFetchTime && node.getTimestamp() < currentTime; | return node.getTimestamp() > lastFetchTime && node.getTimestamp() < currentTime; | ||||
} | } | ||||
@@ -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.data.MetricBucket; | ||||
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap; | import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap; | ||||
import com.alibaba.csp.sentinel.slots.statistic.metric.occupy.OccupiableBucketLeapArray; | 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. | * The basic metric class in Sentinel using a {@link BucketLeapArray} internal. | ||||
@@ -153,33 +154,56 @@ public class ArrayMetric implements Metric { | |||||
@Override | @Override | ||||
public List<MetricNode> details() { | public List<MetricNode> details() { | ||||
List<MetricNode> details = new ArrayList<MetricNode>(); | |||||
List<MetricNode> details = new ArrayList<>(); | |||||
data.currentWindow(); | data.currentWindow(); | ||||
List<WindowWrap<MetricBucket>> list = data.list(); | List<WindowWrap<MetricBucket>> list = data.list(); | ||||
for (WindowWrap<MetricBucket> window : list) { | for (WindowWrap<MetricBucket> window : list) { | ||||
if (window == null) { | if (window == null) { | ||||
continue; | 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<MetricNode> detailsOnCondition(Predicate<Long> timePredicate) { | |||||
List<MetricNode> details = new ArrayList<>(); | |||||
data.currentWindow(); | |||||
List<WindowWrap<MetricBucket>> list = data.list(); | |||||
for (WindowWrap<MetricBucket> 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; | return details; | ||||
} | } | ||||
private MetricNode fromBucket(WindowWrap<MetricBucket> 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 | @Override | ||||
public MetricBucket[] windows() { | public MetricBucket[] windows() { | ||||
data.currentWindow(); | data.currentWindow(); | ||||
@@ -19,6 +19,7 @@ import java.util.List; | |||||
import com.alibaba.csp.sentinel.node.metric.MetricNode; | import com.alibaba.csp.sentinel.node.metric.MetricNode; | ||||
import com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket; | 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. | * Represents a basic structure recording invocation metrics of protected resources. | ||||
@@ -84,6 +85,15 @@ public interface Metric extends DebugSupport { | |||||
*/ | */ | ||||
List<MetricNode> details(); | List<MetricNode> details(); | ||||
/** | |||||
* Generate aggregated metric items that satisfies the time predicate. | |||||
* | |||||
* @param timePredicate time predicate | |||||
* @return aggregated metric items | |||||
* @since 1.7.0 | |||||
*/ | |||||
List<MetricNode> detailsOnCondition(Predicate<Long> timePredicate); | |||||
/** | /** | ||||
* Get the raw window array. | * Get the raw window array. | ||||
* | * | ||||
@@ -16,9 +16,14 @@ | |||||
package com.alibaba.csp.sentinel.slots.statistic.metric; | package com.alibaba.csp.sentinel.slots.statistic.metric; | ||||
import java.util.ArrayList; | 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.base.WindowWrap; | ||||
import com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket; | import com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket; | ||||
import com.alibaba.csp.sentinel.util.function.Predicate; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
@@ -38,7 +43,8 @@ public class ArrayMetricTest { | |||||
@Test | @Test | ||||
public void testOperateArrayMetric() { | public void testOperateArrayMetric() { | ||||
BucketLeapArray leapArray = mock(BucketLeapArray.class); | BucketLeapArray leapArray = mock(BucketLeapArray.class); | ||||
final WindowWrap<MetricBucket> windowWrap = new WindowWrap<MetricBucket>(windowLengthInMs, 0, new MetricBucket()); | |||||
final WindowWrap<MetricBucket> windowWrap = new WindowWrap<MetricBucket>(windowLengthInMs, 0, | |||||
new MetricBucket()); | |||||
when(leapArray.currentWindow()).thenReturn(windowWrap); | when(leapArray.currentWindow()).thenReturn(windowWrap); | ||||
when(leapArray.values()).thenReturn(new ArrayList<MetricBucket>() {{ add(windowWrap.value()); }}); | when(leapArray.values()).thenReturn(new ArrayList<MetricBucket>() {{ add(windowWrap.value()); }}); | ||||
@@ -70,4 +76,45 @@ public class ArrayMetricTest { | |||||
assertEquals(expectedException, metric.exception()); | assertEquals(expectedException, metric.exception()); | ||||
assertEquals(expectedRt, metric.rt()); | assertEquals(expectedRt, metric.rt()); | ||||
} | } | ||||
} | |||||
@Test | |||||
public void testGetMetricDetailsOnCondition() { | |||||
BucketLeapArray leapArray = mock(BucketLeapArray.class); | |||||
// Mock interval=2s, sampleCount=2 | |||||
final WindowWrap<MetricBucket> w1 = new WindowWrap<>(windowLengthInMs, 500, | |||||
new MetricBucket().add(MetricEvent.PASS, 1)); | |||||
final WindowWrap<MetricBucket> w2 = new WindowWrap<>(windowLengthInMs, 1000, | |||||
new MetricBucket().add(MetricEvent.PASS, 2)); | |||||
final WindowWrap<MetricBucket> w3 = new WindowWrap<>(windowLengthInMs, 1500, | |||||
new MetricBucket().add(MetricEvent.PASS, 3)); | |||||
final WindowWrap<MetricBucket> w4 = new WindowWrap<>(windowLengthInMs, 2000, | |||||
new MetricBucket().add(MetricEvent.PASS, 4)); | |||||
List<WindowWrap<MetricBucket>> 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<MetricNode> metricNodes = metric.detailsOnCondition(new Predicate<Long>() { | |||||
@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<Long>() { | |||||
@Override | |||||
public boolean test(Long t) { | |||||
return t >= 2500; | |||||
} | |||||
}); | |||||
assertEquals(0, metricNodes.size()); | |||||
} | |||||
} |