Skip to content

Commit 63c475f

Browse files
authored
Improve Node and Metric interface to support conditional metric retrieval (alibaba#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 <[email protected]>
1 parent 314ff74 commit 63c475f

File tree

5 files changed

+114
-16
lines changed

5 files changed

+114
-16
lines changed

sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/Node.java

+11
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
*/
1616
package com.alibaba.csp.sentinel.node;
1717

18+
import java.util.List;
1819
import java.util.Map;
1920

2021
import com.alibaba.csp.sentinel.Entry;
2122
import com.alibaba.csp.sentinel.node.metric.MetricNode;
2223
import com.alibaba.csp.sentinel.slots.statistic.metric.DebugSupport;
24+
import com.alibaba.csp.sentinel.util.function.Predicate;
2325

2426
/**
2527
* Holds real-time statistics for resources.
@@ -146,6 +148,15 @@ public interface Node extends OccupySupport, DebugSupport {
146148
*/
147149
Map<Long, MetricNode> metrics();
148150

151+
/**
152+
* Fetch all raw metric items that satisfies the time predicate.
153+
*
154+
* @param timePredicate time predicate
155+
* @return raw metric items that satisfies the time predicate
156+
* @since 1.7.0
157+
*/
158+
List<MetricNode> rawMetricsInMin(Predicate<Long> timePredicate);
159+
149160
/**
150161
* Add pass count.
151162
*

sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.alibaba.csp.sentinel.slots.statistic.metric.ArrayMetric;
2121
import com.alibaba.csp.sentinel.slots.statistic.metric.Metric;
2222
import com.alibaba.csp.sentinel.util.TimeUtil;
23+
import com.alibaba.csp.sentinel.util.function.Predicate;
2324

2425
import java.util.List;
2526
import java.util.Map;
@@ -131,6 +132,11 @@ public Map<Long, MetricNode> metrics() {
131132
return metrics;
132133
}
133134

135+
@Override
136+
public List<MetricNode> rawMetricsInMin(Predicate<Long> timePredicate) {
137+
return rollingCounterInMinute.detailsOnCondition(timePredicate);
138+
}
139+
134140
private boolean isNodeInTime(MetricNode node, long currentTime) {
135141
return node.getTimestamp() > lastFetchTime && node.getTimestamp() < currentTime;
136142
}

sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java

+38-14
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket;
2626
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
2727
import com.alibaba.csp.sentinel.slots.statistic.metric.occupy.OccupiableBucketLeapArray;
28+
import com.alibaba.csp.sentinel.util.function.Predicate;
2829

2930
/**
3031
* The basic metric class in Sentinel using a {@link BucketLeapArray} internal.
@@ -153,33 +154,56 @@ public long minRt() {
153154

154155
@Override
155156
public List<MetricNode> details() {
156-
List<MetricNode> details = new ArrayList<MetricNode>();
157+
List<MetricNode> details = new ArrayList<>();
157158
data.currentWindow();
158159
List<WindowWrap<MetricBucket>> list = data.list();
159160
for (WindowWrap<MetricBucket> window : list) {
160161
if (window == null) {
161162
continue;
162163
}
163-
MetricNode node = new MetricNode();
164-
node.setBlockQps(window.value().block());
165-
node.setExceptionQps(window.value().exception());
166-
node.setPassQps(window.value().pass());
167-
long successQps = window.value().success();
168-
node.setSuccessQps(successQps);
169-
if (successQps != 0) {
170-
node.setRt(window.value().rt() / successQps);
171-
} else {
172-
node.setRt(window.value().rt());
164+
165+
details.add(fromBucket(window));
166+
}
167+
168+
return details;
169+
}
170+
171+
@Override
172+
public List<MetricNode> detailsOnCondition(Predicate<Long> timePredicate) {
173+
List<MetricNode> details = new ArrayList<>();
174+
data.currentWindow();
175+
List<WindowWrap<MetricBucket>> list = data.list();
176+
for (WindowWrap<MetricBucket> window : list) {
177+
if (window == null) {
178+
continue;
179+
}
180+
if (timePredicate != null && !timePredicate.test(window.windowStart())) {
181+
continue;
173182
}
174-
node.setTimestamp(window.windowStart());
175-
node.setOccupiedPassQps(window.value().occupiedPass());
176183

177-
details.add(node);
184+
details.add(fromBucket(window));
178185
}
179186

180187
return details;
181188
}
182189

190+
private MetricNode fromBucket(WindowWrap<MetricBucket> wrap) {
191+
MetricNode node = new MetricNode();
192+
node.setBlockQps(wrap.value().block());
193+
node.setExceptionQps(wrap.value().exception());
194+
node.setPassQps(wrap.value().pass());
195+
long successQps = wrap.value().success();
196+
node.setSuccessQps(successQps);
197+
if (successQps != 0) {
198+
node.setRt(wrap.value().rt() / successQps);
199+
} else {
200+
node.setRt(wrap.value().rt());
201+
}
202+
node.setTimestamp(wrap.windowStart());
203+
node.setOccupiedPassQps(wrap.value().occupiedPass());
204+
return node;
205+
}
206+
183207
@Override
184208
public MetricBucket[] windows() {
185209
data.currentWindow();

sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java

+10
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.alibaba.csp.sentinel.node.metric.MetricNode;
2121
import com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket;
22+
import com.alibaba.csp.sentinel.util.function.Predicate;
2223

2324
/**
2425
* Represents a basic structure recording invocation metrics of protected resources.
@@ -84,6 +85,15 @@ public interface Metric extends DebugSupport {
8485
*/
8586
List<MetricNode> details();
8687

88+
/**
89+
* Generate aggregated metric items that satisfies the time predicate.
90+
*
91+
* @param timePredicate time predicate
92+
* @return aggregated metric items
93+
* @since 1.7.0
94+
*/
95+
List<MetricNode> detailsOnCondition(Predicate<Long> timePredicate);
96+
8797
/**
8898
* Get the raw window array.
8999
*

sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetricTest.java

+49-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@
1616
package com.alibaba.csp.sentinel.slots.statistic.metric;
1717

1818
import java.util.ArrayList;
19+
import java.util.Arrays;
20+
import java.util.List;
1921

22+
import com.alibaba.csp.sentinel.node.metric.MetricNode;
23+
import com.alibaba.csp.sentinel.slots.statistic.MetricEvent;
2024
import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
2125
import com.alibaba.csp.sentinel.slots.statistic.data.MetricBucket;
26+
import com.alibaba.csp.sentinel.util.function.Predicate;
2227

2328
import org.junit.Test;
2429

@@ -38,7 +43,8 @@ public class ArrayMetricTest {
3843
@Test
3944
public void testOperateArrayMetric() {
4045
BucketLeapArray leapArray = mock(BucketLeapArray.class);
41-
final WindowWrap<MetricBucket> windowWrap = new WindowWrap<MetricBucket>(windowLengthInMs, 0, new MetricBucket());
46+
final WindowWrap<MetricBucket> windowWrap = new WindowWrap<MetricBucket>(windowLengthInMs, 0,
47+
new MetricBucket());
4248
when(leapArray.currentWindow()).thenReturn(windowWrap);
4349
when(leapArray.values()).thenReturn(new ArrayList<MetricBucket>() {{ add(windowWrap.value()); }});
4450

@@ -70,4 +76,45 @@ public void testOperateArrayMetric() {
7076
assertEquals(expectedException, metric.exception());
7177
assertEquals(expectedRt, metric.rt());
7278
}
73-
}
79+
80+
@Test
81+
public void testGetMetricDetailsOnCondition() {
82+
BucketLeapArray leapArray = mock(BucketLeapArray.class);
83+
// Mock interval=2s, sampleCount=2
84+
final WindowWrap<MetricBucket> w1 = new WindowWrap<>(windowLengthInMs, 500,
85+
new MetricBucket().add(MetricEvent.PASS, 1));
86+
final WindowWrap<MetricBucket> w2 = new WindowWrap<>(windowLengthInMs, 1000,
87+
new MetricBucket().add(MetricEvent.PASS, 2));
88+
final WindowWrap<MetricBucket> w3 = new WindowWrap<>(windowLengthInMs, 1500,
89+
new MetricBucket().add(MetricEvent.PASS, 3));
90+
final WindowWrap<MetricBucket> w4 = new WindowWrap<>(windowLengthInMs, 2000,
91+
new MetricBucket().add(MetricEvent.PASS, 4));
92+
List<WindowWrap<MetricBucket>> buckets = Arrays.asList(w1, w2, w3, w4);
93+
when(leapArray.currentWindow()).thenReturn(w4);
94+
when(leapArray.list()).thenReturn(buckets);
95+
96+
ArrayMetric metric = new ArrayMetric(leapArray);
97+
98+
// Empty condition -> retrieve all
99+
assertEquals(4, metric.detailsOnCondition(null).size());
100+
// Normal condition
101+
List<MetricNode> metricNodes = metric.detailsOnCondition(new Predicate<Long>() {
102+
@Override
103+
public boolean test(Long t) {
104+
return t >= 1500;
105+
}
106+
});
107+
assertEquals(2, metricNodes.size());
108+
assertEquals(3, metricNodes.get(0).getPassQps());
109+
assertEquals(4, metricNodes.get(1).getPassQps());
110+
111+
// Future condition
112+
metricNodes = metric.detailsOnCondition(new Predicate<Long>() {
113+
@Override
114+
public boolean test(Long t) {
115+
return t >= 2500;
116+
}
117+
});
118+
assertEquals(0, metricNodes.size());
119+
}
120+
}

0 commit comments

Comments
 (0)