* 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; | |||
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<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. | |||
* | |||
@@ -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<MetricNode> rawMetricsInMin(Predicate<Long> timePredicate) { | |||
return rollingCounterInMinute.detailsOnCondition(timePredicate); | |||
} | |||
private boolean isNodeInTime(MetricNode node, long 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.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<MetricNode> details() { | |||
List<MetricNode> details = new ArrayList<MetricNode>(); | |||
List<MetricNode> details = new ArrayList<>(); | |||
data.currentWindow(); | |||
List<WindowWrap<MetricBucket>> list = data.list(); | |||
for (WindowWrap<MetricBucket> 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<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; | |||
} | |||
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 | |||
public MetricBucket[] windows() { | |||
data.currentWindow(); | |||
@@ -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<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. | |||
* | |||
@@ -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<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.values()).thenReturn(new ArrayList<MetricBucket>() {{ add(windowWrap.value()); }}); | |||
@@ -70,4 +76,45 @@ public class ArrayMetricTest { | |||
assertEquals(expectedException, metric.exception()); | |||
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()); | |||
} | |||
} |