diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/ClusterMetricNode.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/ClusterMetricNode.java new file mode 100644 index 00000000..b585f804 --- /dev/null +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/ClusterMetricNode.java @@ -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 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 getTopParams() { + return topParams; + } + + public ClusterMetricNode setTopParams(Map 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 + + '}'; + } +} diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/ClusterMetricNodeGenerator.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/ClusterMetricNodeGenerator.java new file mode 100644 index 00000000..84229b87 --- /dev/null +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/ClusterMetricNodeGenerator.java @@ -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> generateCurrentNodeMap(String namespace) { + Map> map = new HashMap<>(); + Set flowIds = ClusterFlowRuleManager.getFlowIdSet(namespace); + Set 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> map, ClusterMetricNode node) { + List 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(0)); + } + return new ClusterMetricNode() + .setFlowId(flowId) + .setResourceName(rule.getResource()) + .setTimestamp(TimeUtil.currentTimeMillis()) + .setTopParams(metric.getTopValues(5)); + } +} diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetric.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetric.java index 1768d8ea..12710e71 100644 --- a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetric.java +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/flow/statistic/metric/ClusterParamMetric.java @@ -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 getTopValues(int number) { + metric.currentWindow(); + List> buckets = metric.values(); + + Map result = new HashMap<>(buckets.size()); + + for (CacheMap b : buckets) { + Set 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> set = result.entrySet(); + List> list = new ArrayList<>(set); + Collections.sort(list, new Comparator>() { + @Override + public int compare(Entry a, + Entry b) { + return (int)(b.getValue() == null ? 0 : b.getValue()) - (int)(a.getValue() == null ? 0 : a.getValue()); + } + }); + + Map doubleResult = new HashMap(); + + int size = list.size() > number ? number : list.size(); + for (int i = 0; i < size; i++) { + Map.Entry x = list.get(i); + if (x.getValue() == 0) { + break; + } + doubleResult.put(x.getKey(), ((double)x.getValue()) / metric.getIntervalInSecond()); + } + + return doubleResult; + } } diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/FetchClusterMetricCommandHandler.java b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/FetchClusterMetricCommandHandler.java new file mode 100644 index 00000000..4d9f7997 --- /dev/null +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/java/com/alibaba/csp/sentinel/cluster/server/command/handler/FetchClusterMetricCommandHandler.java @@ -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 { + + @Override + public CommandResponse 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)) + ); + } +} diff --git a/sentinel-cluster/sentinel-cluster-server-default/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler b/sentinel-cluster/sentinel-cluster-server-default/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler index 5fe1f5b8..0ebc4f55 100755 --- a/sentinel-cluster/sentinel-cluster-server-default/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler +++ b/sentinel-cluster/sentinel-cluster-server-default/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler @@ -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 \ No newline at end of file +com.alibaba.csp.sentinel.cluster.server.command.handler.FetchClusterServerInfoCommandHandler +com.alibaba.csp.sentinel.cluster.server.command.handler.FetchClusterMetricCommandHandler \ No newline at end of file