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:
Eric Zhao 2019-10-25 16:03:07 +08:00 committed by GitHub
parent f0e3348caf
commit 74a40aa285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 115 additions and 17 deletions

View File

@ -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.
* *

View File

@ -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;
} }

View File

@ -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());
}
node.setTimestamp(window.windowStart());
node.setOccupiedPassQps(window.value().occupiedPass());
details.add(node); details.add(fromBucket(window));
} }
return details; 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 @Override
public MetricBucket[] windows() { public MetricBucket[] windows() {
data.currentWindow(); data.currentWindow();

View File

@ -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.
* *

View File

@ -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());
}
}