Browse Source

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>
master
Eric Zhao GitHub 5 years ago
parent
commit
74a40aa285
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 16 deletions
  1. +11
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/Node.java
  2. +6
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java
  3. +38
    -14
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java
  4. +10
    -0
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java
  5. +49
    -2
      sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetricTest.java

+ 11
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/Node.java View File

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


+ 6
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java 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.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;
}


+ 38
- 14
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java 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.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();


+ 10
- 0
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java View File

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


+ 49
- 2
sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetricTest.java View File

@@ -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…
Cancel
Save