- 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; | 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.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.base.LongAdder; | ||||
import com.alibaba.csp.sentinel.slots.statistic.cache.CacheMap; | import com.alibaba.csp.sentinel.slots.statistic.cache.CacheMap; | ||||
import com.alibaba.csp.sentinel.util.AssertUtil; | import com.alibaba.csp.sentinel.util.AssertUtil; | ||||
@@ -79,4 +87,48 @@ public class ClusterParamMetric { | |||||
public double getAvg(Object value) { | public double getAvg(Object value) { | ||||
return getSum(value) / metric.getIntervalInSecond(); | 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.ModifyServerNamespaceSetHandler | ||||
com.alibaba.csp.sentinel.cluster.server.command.handler.ModifyClusterFlowRulesCommandHandler | 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.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 |