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;
|
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());
|
|
||||||
}
|
|
||||||
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();
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue