- Also add top-K frequent parameter statistic support Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -0,0 +1,112 @@ | |||
/* | |||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.cluster.flow.statistic; | |||
import java.util.Map; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.4.1 | |||
*/ | |||
public class ClusterMetricNode { | |||
private long timestamp; | |||
private String resourceName; | |||
private long flowId; | |||
private double passQps; | |||
private double blockQps; | |||
private long rt; | |||
private Map<Object, Double> topParams; | |||
public long getTimestamp() { | |||
return timestamp; | |||
} | |||
public ClusterMetricNode setTimestamp(long timestamp) { | |||
this.timestamp = timestamp; | |||
return this; | |||
} | |||
public String getResourceName() { | |||
return resourceName; | |||
} | |||
public ClusterMetricNode setResourceName(String resourceName) { | |||
this.resourceName = resourceName; | |||
return this; | |||
} | |||
public long getFlowId() { | |||
return flowId; | |||
} | |||
public ClusterMetricNode setFlowId(long flowId) { | |||
this.flowId = flowId; | |||
return this; | |||
} | |||
public double getPassQps() { | |||
return passQps; | |||
} | |||
public ClusterMetricNode setPassQps(double passQps) { | |||
this.passQps = passQps; | |||
return this; | |||
} | |||
public double getBlockQps() { | |||
return blockQps; | |||
} | |||
public ClusterMetricNode setBlockQps(double blockQps) { | |||
this.blockQps = blockQps; | |||
return this; | |||
} | |||
public long getRt() { | |||
return rt; | |||
} | |||
public ClusterMetricNode setRt(long rt) { | |||
this.rt = rt; | |||
return this; | |||
} | |||
public Map<Object, Double> getTopParams() { | |||
return topParams; | |||
} | |||
public ClusterMetricNode setTopParams(Map<Object, Double> topParams) { | |||
this.topParams = topParams; | |||
return this; | |||
} | |||
@Override | |||
public String toString() { | |||
return "ClusterMetricNode{" + | |||
"timestamp=" + timestamp + | |||
", resourceName='" + resourceName + '\'' + | |||
", flowId=" + flowId + | |||
", passQps=" + passQps + | |||
", blockQps=" + blockQps + | |||
", rt=" + rt + | |||
", topParams=" + topParams + | |||
'}'; | |||
} | |||
} |
@@ -0,0 +1,106 @@ | |||
/* | |||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.cluster.flow.statistic; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager; | |||
import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterParamFlowRuleManager; | |||
import com.alibaba.csp.sentinel.cluster.flow.statistic.data.ClusterFlowEvent; | |||
import com.alibaba.csp.sentinel.cluster.flow.statistic.metric.ClusterMetric; | |||
import com.alibaba.csp.sentinel.cluster.flow.statistic.metric.ClusterParamMetric; | |||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; | |||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; | |||
import com.alibaba.csp.sentinel.util.TimeUtil; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.4.1 | |||
*/ | |||
public class ClusterMetricNodeGenerator { | |||
public static Map<String, List<ClusterMetricNode>> generateCurrentNodeMap(String namespace) { | |||
Map<String, List<ClusterMetricNode>> map = new HashMap<>(); | |||
Set<Long> flowIds = ClusterFlowRuleManager.getFlowIdSet(namespace); | |||
Set<Long> paramFlowIds = ClusterParamFlowRuleManager.getFlowIdSet(namespace); | |||
for (Long id : flowIds) { | |||
ClusterMetricNode node = flowToMetricNode(id); | |||
if (node == null) { | |||
continue; | |||
} | |||
putToMap(map, node); | |||
} | |||
for (Long id : paramFlowIds) { | |||
ClusterMetricNode node = paramToMetricNode(id); | |||
if (node == null) { | |||
continue; | |||
} | |||
putToMap(map, node); | |||
} | |||
return map; | |||
} | |||
private static void putToMap(Map<String, List<ClusterMetricNode>> map, ClusterMetricNode node) { | |||
List<ClusterMetricNode> nodeList = map.get(node.getResourceName()); | |||
if (nodeList == null) { | |||
nodeList = new ArrayList<>(); | |||
map.put(node.getResourceName(), nodeList); | |||
} | |||
nodeList.add(node); | |||
} | |||
public static ClusterMetricNode flowToMetricNode(long flowId) { | |||
FlowRule rule = ClusterFlowRuleManager.getFlowRuleById(flowId); | |||
if (rule == null) { | |||
return null; | |||
} | |||
ClusterMetric metric = ClusterMetricStatistics.getMetric(flowId); | |||
if (metric == null) { | |||
return new ClusterMetricNode().setFlowId(flowId) | |||
.setResourceName(rule.getResource()); | |||
} | |||
return new ClusterMetricNode() | |||
.setFlowId(flowId) | |||
.setResourceName(rule.getResource()) | |||
.setBlockQps(metric.getAvg(ClusterFlowEvent.BLOCK)) | |||
.setPassQps(metric.getAvg(ClusterFlowEvent.PASS)) | |||
.setTimestamp(TimeUtil.currentTimeMillis()); | |||
} | |||
public static ClusterMetricNode paramToMetricNode(long flowId) { | |||
ParamFlowRule rule = ClusterParamFlowRuleManager.getParamRuleById(flowId); | |||
if (rule == null) { | |||
return null; | |||
} | |||
ClusterParamMetric metric = ClusterParamMetricStatistics.getMetric(flowId); | |||
if (metric == null) { | |||
return new ClusterMetricNode().setFlowId(flowId) | |||
.setResourceName(rule.getResource()) | |||
.setTimestamp(TimeUtil.currentTimeMillis()) | |||
.setTopParams(new HashMap<Object, Double>(0)); | |||
} | |||
return new ClusterMetricNode() | |||
.setFlowId(flowId) | |||
.setResourceName(rule.getResource()) | |||
.setTimestamp(TimeUtil.currentTimeMillis()) | |||
.setTopParams(metric.getTopValues(5)); | |||
} | |||
} |
@@ -15,8 +15,16 @@ | |||
*/ | |||
package com.alibaba.csp.sentinel.cluster.flow.statistic.metric; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
import java.util.Set; | |||
import com.alibaba.csp.sentinel.cluster.flow.statistic.data.ClusterFlowEvent; | |||
import com.alibaba.csp.sentinel.slots.statistic.base.LongAdder; | |||
import com.alibaba.csp.sentinel.slots.statistic.cache.CacheMap; | |||
import com.alibaba.csp.sentinel.util.AssertUtil; | |||
@@ -79,4 +87,48 @@ public class ClusterParamMetric { | |||
public double getAvg(Object value) { | |||
return getSum(value) / metric.getIntervalInSecond(); | |||
} | |||
public Map<Object, Double> getTopValues(int number) { | |||
metric.currentWindow(); | |||
List<CacheMap<Object, LongAdder>> buckets = metric.values(); | |||
Map<Object, Long> result = new HashMap<>(buckets.size()); | |||
for (CacheMap<Object, LongAdder> b : buckets) { | |||
Set<Object> subSet = b.keySet(true); | |||
for (Object o : subSet) { | |||
Long count = result.get(o); | |||
if (count == null) { | |||
count = getCount(b.get(o)); | |||
} else { | |||
count += getCount(b.get(o)); | |||
} | |||
result.put(o, count); | |||
} | |||
} | |||
// After merge, get the top set one. | |||
Set<Entry<Object, Long>> set = result.entrySet(); | |||
List<Entry<Object, Long>> list = new ArrayList<>(set); | |||
Collections.sort(list, new Comparator<Entry<Object, Long>>() { | |||
@Override | |||
public int compare(Entry<Object, Long> a, | |||
Entry<Object, Long> b) { | |||
return (int)(b.getValue() == null ? 0 : b.getValue()) - (int)(a.getValue() == null ? 0 : a.getValue()); | |||
} | |||
}); | |||
Map<Object, Double> doubleResult = new HashMap<Object, Double>(); | |||
int size = list.size() > number ? number : list.size(); | |||
for (int i = 0; i < size; i++) { | |||
Map.Entry<Object, Long> x = list.get(i); | |||
if (x.getValue() == 0) { | |||
break; | |||
} | |||
doubleResult.put(x.getKey(), ((double)x.getValue()) / metric.getIntervalInSecond()); | |||
} | |||
return doubleResult; | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* Copyright 1999-2018 Alibaba Group Holding Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.cluster.server.command.handler; | |||
import com.alibaba.csp.sentinel.cluster.flow.statistic.ClusterMetricNodeGenerator; | |||
import com.alibaba.csp.sentinel.command.CommandHandler; | |||
import com.alibaba.csp.sentinel.command.CommandRequest; | |||
import com.alibaba.csp.sentinel.command.CommandResponse; | |||
import com.alibaba.csp.sentinel.command.annotation.CommandMapping; | |||
import com.alibaba.csp.sentinel.util.StringUtil; | |||
import com.alibaba.fastjson.JSON; | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.4.1 | |||
*/ | |||
@CommandMapping(name = "cluster/server/metricList") | |||
public class FetchClusterMetricCommandHandler implements CommandHandler<String> { | |||
@Override | |||
public CommandResponse<String> handle(CommandRequest request) { | |||
String namespace = request.getParam("namespace"); | |||
if (StringUtil.isEmpty(namespace)) { | |||
return CommandResponse.ofFailure(new IllegalArgumentException("failed: namespace cannot be empty")); | |||
} | |||
return CommandResponse.ofSuccess( | |||
JSON.toJSONString(ClusterMetricNodeGenerator.generateCurrentNodeMap(namespace)) | |||
); | |||
} | |||
} |
@@ -6,4 +6,5 @@ com.alibaba.csp.sentinel.cluster.server.command.handler.ModifyClusterServerTrans | |||
com.alibaba.csp.sentinel.cluster.server.command.handler.ModifyServerNamespaceSetHandler | |||
com.alibaba.csp.sentinel.cluster.server.command.handler.ModifyClusterFlowRulesCommandHandler | |||
com.alibaba.csp.sentinel.cluster.server.command.handler.ModifyClusterParamFlowRulesCommandHandler | |||
com.alibaba.csp.sentinel.cluster.server.command.handler.FetchClusterServerInfoCommandHandler | |||
com.alibaba.csp.sentinel.cluster.server.command.handler.FetchClusterServerInfoCommandHandler | |||
com.alibaba.csp.sentinel.cluster.server.command.handler.FetchClusterMetricCommandHandler |