Improve Node and Metric interface to support conditional metric retrieval (#1115)
* 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>
This commit is contained in:
parent
f0e3348caf
commit
74a40aa285
|
|
@ -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());
|
||||
}
|
||||
node.setTimestamp(window.windowStart());
|
||||
node.setOccupiedPassQps(window.value().occupiedPass());
|
||||
|
||||
details.add(node);
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue