You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by il...@apache.org on 2019/09/19 08:27:34 UTC

[dubbo-admin] branch develop updated: Feature#23 (#437)

This is an automated email from the ASF dual-hosted git repository.

iluo pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-admin.git


The following commit(s) were added to refs/heads/develop by this push:
     new 368d9dd  Feature#23 (#437)
368d9dd is described below

commit 368d9dd17ea61b759fabb0e0c0f7bd5f08c092d7
Author: 孙不服 <su...@163.com>
AuthorDate: Thu Sep 19 16:27:29 2019 +0800

    Feature#23 (#437)
    
    * ace 行号栏 z-index 过高 #358
    
    * Add a graph to display the call relationship of all applications #23
    
    * code style
---
 .../admin/controller/MetricsCollectController.java |  12 +-
 .../apache/dubbo/admin/model/dto/RelationDTO.java  | 216 +++++++++++++++++++++
 .../apache/dubbo/admin/service/MetricsService.java |  24 +++
 .../admin/service/impl/MetricsServiceImpl.java     | 119 ++++++++++++
 dubbo-admin-ui/src/api/menu.js                     |  12 +-
 .../src/components/metrics/ServiceRelation.vue     | 118 +++++++++++
 dubbo-admin-ui/src/lang/en.js                      |   3 +
 dubbo-admin-ui/src/lang/zh.js                      |   3 +
 dubbo-admin-ui/src/router/index.js                 |   8 +-
 9 files changed, 510 insertions(+), 5 deletions(-)

diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java
index 9dfc43c..9c061a2 100644
--- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java
@@ -24,7 +24,9 @@ import org.apache.dubbo.admin.common.util.Tool;
 import org.apache.dubbo.admin.model.domain.Consumer;
 import org.apache.dubbo.admin.model.domain.Provider;
 import org.apache.dubbo.admin.model.dto.MetricDTO;
+import org.apache.dubbo.admin.model.dto.RelationDTO;
 import org.apache.dubbo.admin.service.ConsumerService;
+import org.apache.dubbo.admin.service.MetricsService;
 import org.apache.dubbo.admin.service.ProviderService;
 import org.apache.dubbo.admin.service.impl.MetrcisCollectServiceImpl;
 import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
@@ -41,8 +43,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-
-
 @RestController
 @RequestMapping("/api/{env}/metrics")
 public class MetricsCollectController {
@@ -53,6 +53,9 @@ public class MetricsCollectController {
     @Autowired
     private ConsumerService consumerService;
 
+    @Autowired
+    private MetricsService metricsService;
+
     @RequestMapping(method = RequestMethod.POST)
     public String metricsCollect(@RequestParam String group, @PathVariable String env) {
         MetrcisCollectServiceImpl service = new MetrcisCollectServiceImpl();
@@ -61,6 +64,11 @@ public class MetricsCollectController {
         return service.invoke(group).toString();
     }
 
+    @RequestMapping(value = "/relation", method = RequestMethod.GET)
+    public RelationDTO getApplicationRelation(){
+        return metricsService.getApplicationRelation();
+    }
+
     private String getOnePortMessage(String group, String ip, String port, String protocol) {
         MetrcisCollectServiceImpl metrcisCollectService = new MetrcisCollectServiceImpl();
         metrcisCollectService.setUrl(protocol + "://" + ip + ":" + port +"?scope=remote&cache=true");
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/model/dto/RelationDTO.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/model/dto/RelationDTO.java
new file mode 100644
index 0000000..90edcf1
--- /dev/null
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/model/dto/RelationDTO.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dubbo.admin.model.dto;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * relation about node for relation graph
+ */
+public class RelationDTO {
+
+    private List<Categories> categories;
+    private List<Node> nodes;
+    private List<Link> links;
+
+    public static final Categories CONSUMER_CATEGORIES = new RelationDTO.Categories(0, "consumer", "consumer");;
+    public static final Categories PROVIDER_CATEGORIES = new RelationDTO.Categories(1, "provider", "provider");
+    public static final Categories CONSUMER_AND_PROVIDER_CATEGORIES = new RelationDTO.Categories(2, "consumer and provider", "consumer and provider");
+
+    public static final List<RelationDTO.Categories> CATEGORIES_LIST = Arrays.asList(CONSUMER_CATEGORIES, PROVIDER_CATEGORIES, CONSUMER_AND_PROVIDER_CATEGORIES);
+
+    public RelationDTO() {
+    }
+
+    public RelationDTO(List<Node> nodes, List<Link> links) {
+        this.categories = CATEGORIES_LIST;
+        this.nodes = nodes;
+        this.links = links;
+    }
+
+    public static class Categories {
+        private Integer index;
+        private String name;
+        private String base;
+
+        public Categories() {
+        }
+
+        public Categories(Integer index, String name, String base) {
+            this.index = index;
+            this.name = name;
+            this.base = base;
+        }
+
+        public Integer getIndex() {
+            return index;
+        }
+
+        public void setIndex(Integer index) {
+            this.index = index;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getBase() {
+            return base;
+        }
+
+        public void setBase(String base) {
+            this.base = base;
+        }
+    }
+
+    public static class Node {
+
+        private Integer index;
+        private String name;
+        private int category;
+
+        public Node() {
+        }
+
+        public Node(Integer index, String name, int category) {
+            this.index = index;
+            this.name = name;
+            this.category = category;
+        }
+
+        public Integer getIndex() {
+            return index;
+        }
+
+        public void setIndex(Integer index) {
+            this.index = index;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public int getCategory() {
+            return category;
+        }
+
+        public void setCategory(int category) {
+            this.category = category;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Node node = (Node) o;
+            return category == node.category &&
+                    index.equals(node.index) &&
+                    name.equals(node.name);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(index, name, category);
+        }
+    }
+
+    public static class Link {
+
+        private int source;
+        private int target;
+
+        public Link() {
+        }
+
+        public Link(int source, int target) {
+            this.source = source;
+            this.target = target;
+        }
+
+        public int getSource() {
+            return source;
+        }
+
+        public void setSource(int source) {
+            this.source = source;
+        }
+
+        public int getTarget() {
+            return target;
+        }
+
+        public void setTarget(int target) {
+            this.target = target;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            Link link = (Link) o;
+            return source == link.source &&
+                    target == link.target;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(source, target);
+        }
+
+        @Override
+        public String toString() {
+            return "Link{" +
+                    "source=" + source +
+                    ", target=" + target +
+                    '}';
+        }
+    }
+
+    public List<Categories> getCategories() {
+        return categories;
+    }
+
+    public void setCategories(List<Categories> categories) {
+        this.categories = categories;
+    }
+
+    public List<Node> getNodes() {
+        return nodes;
+    }
+
+    public void setNodes(List<Node> nodes) {
+        this.nodes = nodes;
+    }
+
+    public List<Link> getLinks() {
+        return links;
+    }
+
+    public void setLinks(List<Link> links) {
+        this.links = links;
+    }
+}
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/MetricsService.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/MetricsService.java
new file mode 100644
index 0000000..6da25e0
--- /dev/null
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/MetricsService.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dubbo.admin.service;
+
+import org.apache.dubbo.admin.model.dto.RelationDTO;
+
+public interface MetricsService {
+
+    RelationDTO getApplicationRelation();
+}
diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/MetricsServiceImpl.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/MetricsServiceImpl.java
new file mode 100644
index 0000000..dee5f9f
--- /dev/null
+++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/MetricsServiceImpl.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dubbo.admin.service.impl;
+
+import org.apache.dubbo.admin.model.domain.Consumer;
+import org.apache.dubbo.admin.model.domain.Provider;
+import org.apache.dubbo.admin.model.dto.RelationDTO;
+import org.apache.dubbo.admin.service.ConsumerService;
+import org.apache.dubbo.admin.service.MetricsService;
+import org.apache.dubbo.admin.service.ProviderService;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Component
+public class MetricsServiceImpl implements MetricsService {
+
+    @Autowired
+    private ConsumerService consumerService;
+    @Autowired
+    private ProviderService providerService;
+
+    @Override
+    public RelationDTO getApplicationRelation() {
+
+        List<Consumer> consumerList = consumerService.findAll();
+        List<Provider> providerList = providerService.findAll();
+
+        int index = 0;
+        // collect all service
+        Set<String> serviceSet = new HashSet<>();
+
+        // collect consumer's nodes map <application, node>
+        Map<String, RelationDTO.Node> consumerNodeMap = new HashMap<>();
+        // collect consumer's service and applications map <service, set<application>>
+        Map<String, Set<String>> consumerServiceApplicationMap = new HashMap<>();
+        for (Consumer consumer : consumerList) {
+            String application = consumer.getApplication();
+            if (!consumerNodeMap.keySet().contains(application)) {
+                RelationDTO.Node node = new RelationDTO.Node(index, application, RelationDTO.CONSUMER_CATEGORIES.getIndex());
+                consumerNodeMap.put(application, node);
+                index++;
+            }
+            String service = consumer.getService();
+            serviceSet.add(service);
+            consumerServiceApplicationMap.computeIfAbsent(service, s -> new HashSet<>());
+            consumerServiceApplicationMap.get(service).add(application);
+        }
+        // collect provider's nodes
+        Map<String, RelationDTO.Node> providerNodeMap = new HashMap<>();
+        // collect provider's service and applications map <service, set<application>>
+        Map<String, Set<String>> providerServiceApplicationMap = new HashMap<>();
+        for (Provider provider : providerList) {
+            String application = provider.getApplication();
+            if (!providerNodeMap.keySet().contains(application)) {
+                RelationDTO.Node node = new RelationDTO.Node(index, application, RelationDTO.PROVIDER_CATEGORIES.getIndex());
+                providerNodeMap.put(application, node);
+                index++;
+            }
+            String service = provider.getService();
+            serviceSet.add(service);
+            providerServiceApplicationMap.computeIfAbsent(service, s -> new HashSet<>());
+            providerServiceApplicationMap.get(service).add(application);
+        }
+        // merge provider's nodes and consumer's nodes
+        Map<String, RelationDTO.Node> nodeMap = new HashMap<>(consumerNodeMap);
+        for (Map.Entry<String, RelationDTO.Node> entry : providerNodeMap.entrySet()) {
+            if (nodeMap.get(entry.getKey()) != null) {
+                nodeMap.get(entry.getKey()).setCategory(RelationDTO.CONSUMER_AND_PROVIDER_CATEGORIES.getIndex());
+            } else {
+                nodeMap.put(entry.getKey(), entry.getValue());
+            }
+        }
+        // build link by same service
+        Set<RelationDTO.Link> linkSet = new HashSet<>();
+        for (String service : serviceSet) {
+            Set<String> consumerApplicationSet = consumerServiceApplicationMap.get(service);
+            Set<String> providerApplicationSet = providerServiceApplicationMap.get(service);
+            if (CollectionUtils.isNotEmpty(consumerApplicationSet) && CollectionUtils.isNotEmpty(providerApplicationSet)) {
+                for (String providerApplication : providerApplicationSet) {
+                    for (String consumerApplication : consumerApplicationSet) {
+                        if (nodeMap.get(consumerApplication) != null && nodeMap.get(providerApplication) != null) {
+                            Integer consumerIndex = nodeMap.get(consumerApplication).getIndex();
+                            Integer providerIndex = nodeMap.get(providerApplication).getIndex();
+                            linkSet.add(new RelationDTO.Link(consumerIndex, providerIndex));
+                        }
+                    }
+                }
+            }
+        }
+        // sort node by index
+        List<RelationDTO.Node> nodeList = nodeMap.values().stream().sorted(Comparator.comparingInt(RelationDTO.Node::getIndex)).collect(Collectors.toList());
+        return new RelationDTO(nodeList, new ArrayList<>(linkSet));
+    }
+}
diff --git a/dubbo-admin-ui/src/api/menu.js b/dubbo-admin-ui/src/api/menu.js
index fba2a05..3403f64 100644
--- a/dubbo-admin-ui/src/api/menu.js
+++ b/dubbo-admin-ui/src/api/menu.js
@@ -23,7 +23,7 @@ const Menu = [
     group: 'governance',
     items: [
       { title: 'routingRule', path: '/governance/routingRule' },
-      {title: 'tagRule', path: '/governance/tagRule', badge: 'new'},
+      { title: 'tagRule', path: '/governance/tagRule', badge: 'new' },
       { title: 'accessControl', path: '/governance/access' },
       { title: 'dynamicConfig', path: '/governance/config' },
       { title: 'weightAdjust', path: '/governance/weight' },
@@ -32,7 +32,15 @@ const Menu = [
   },
   { title: 'serviceTest', path: '/test', icon: 'code' },
   { title: 'serviceMock', path: '/mock', icon: 'build', badge: 'feature' },
-  { title: 'metrics', path: '/metrics', icon: 'show_chart', badge: 'feature' },
+  {
+    title: 'serviceMetrics',
+    path: 'metrics',
+    icon: 'show_chart',
+    items: [
+      { title: 'serviceMetrics', path: '/metrics/index', badge: 'feature' },
+      { title: 'serviceRelation', path: '/metrics/relation', badge: 'new' }
+    ]
+  },
   { title: 'configManage', path: '/management', icon: 'build' }
 ]
 
diff --git a/dubbo-admin-ui/src/components/metrics/ServiceRelation.vue b/dubbo-admin-ui/src/components/metrics/ServiceRelation.vue
new file mode 100644
index 0000000..55dee6c
--- /dev/null
+++ b/dubbo-admin-ui/src/components/metrics/ServiceRelation.vue
@@ -0,0 +1,118 @@
+<!--
+  - Licensed to the Apache Software Foundation (ASF) under one or more
+  - contributor license agreements.  See the NOTICE file distributed with
+  - this work for additional information regarding copyright ownership.
+  - The ASF licenses this file to You 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.
+  -->
+
+<template>
+  <v-container grid-list-xl fluid >
+    <v-layout row wrap>
+      <v-flex lg12>
+        <breadcrumb title="serviceRelation" :items="breads"></breadcrumb>
+      </v-flex>
+    </v-layout>
+
+    <v-flex lg12>
+      <v-card>
+          <div id="chartContent" style="width:100%;height:500%;"/>
+      </v-card>
+    </v-flex>
+
+  </v-container>
+
+</template>
+<script>
+  import Breadcrumb from '@/components/public/Breadcrumb'
+  import axios from 'axios'
+  export default {
+    components: {
+      Breadcrumb,
+      axios
+    },
+    data: () => ({
+      baseURL: '/api/dev',
+      success: null,
+      breads: [
+        {
+          text: 'serviceMetrics',
+          href: ''
+        },
+        {
+          text: 'serviceRelation',
+          href: ''
+        }
+      ],
+      responseData: null
+    }),
+    methods: {
+      initData: function () {
+        // eslint-disable-next-line no-undef
+        this.chartContent = echarts.init(document.getElementById('chartContent'))
+        this.chartContent.showLoading()
+        axios.get(this.baseURL + '/metrics/relation')
+          .then(response => {
+            if (response && response.status === 200) {
+              this.success = true
+              this.responseData = response.data
+              this.responseData.type = 'force'
+              this.initChart(this.responseData)
+            }
+          })
+          .catch(error => {
+            this.success = false
+            this.responseData = error.response.data
+          })
+      },
+      initChart: function (data) {
+        this.chartContent.hideLoading()
+
+        const option = {
+          legend: {
+            top: 'bottom',
+            data: data.categories.map(i => i.name)
+          },
+          series: [{
+            type: 'graph',
+            layout: 'force',
+            animation: false,
+            label: {
+              normal: {
+                show: true,
+                position: 'right'
+              }
+            },
+            draggable: true,
+            data: data.nodes.map(function (node, idx) {
+              node.id = idx
+              return node
+            }),
+            categories: this.responseData.categories,
+            force: {
+              edgeLength: 100,
+              repulsion: 10
+            },
+            edges: data.links,
+            edgeSymbol: ['', 'arrow'],
+            edgeSymbolSize: 7
+          }]
+        }
+        this.chartContent.setOption(option)
+      }
+    },
+    mounted: function () {
+      this.initData()
+    }
+
+  }
+</script>
diff --git a/dubbo-admin-ui/src/lang/en.js b/dubbo-admin-ui/src/lang/en.js
index adc826b..d8f44ef 100644
--- a/dubbo-admin-ui/src/lang/en.js
+++ b/dubbo-admin-ui/src/lang/en.js
@@ -26,7 +26,10 @@ export default {
   loadBalance: 'Load Balance',
   serviceTest: 'Service Test',
   serviceMock: 'Service Mock',
+  serviceMetrics: 'Service Metrics',
+  serviceRelation: 'Service Relation',
   metrics: 'Metrics',
+  relation: 'Relation',
   group: 'Group',
   serviceInfo: 'Service Info',
   providers: 'Providers',
diff --git a/dubbo-admin-ui/src/lang/zh.js b/dubbo-admin-ui/src/lang/zh.js
index e1abcc5..d13f7b6 100644
--- a/dubbo-admin-ui/src/lang/zh.js
+++ b/dubbo-admin-ui/src/lang/zh.js
@@ -18,6 +18,8 @@ export default {
   service: '服务',
   serviceSearch: '服务查询',
   serviceGovernance: '服务治理',
+  serviceMetrics: '服务统计',
+  serviceRelation: '服务关系',
   routingRule: '条件路由',
   tagRule: '标签路由',
   dynamicConfig: '动态配置',
@@ -29,6 +31,7 @@ export default {
   providers: '提供者',
   consumers: '消费者',
   metrics: '统计',
+  relation: '关系',
   group: '组',
   version: '版本',
   app: '应用',
diff --git a/dubbo-admin-ui/src/router/index.js b/dubbo-admin-ui/src/router/index.js
index 35f8a90..4b1d318 100644
--- a/dubbo-admin-ui/src/router/index.js
+++ b/dubbo-admin-ui/src/router/index.js
@@ -29,6 +29,7 @@ import Overrides from '@/components/governance/Overrides'
 import ServiceTest from '@/components/test/ServiceTest'
 import ServiceMock from '@/components/test/ServiceMock'
 import ServiceMetrics from '@/components/metrics/ServiceMetrics'
+import ServiceRelation from '@/components/metrics/ServiceRelation'
 import Management from '@/components/Management'
 
 Vue.use(Router)
@@ -90,11 +91,16 @@ export default new Router({
       component: ServiceMock
     },
     {
-      path: '/metrics',
+      path: '/metrics/index',
       name: 'ServiceMetrics',
       component: ServiceMetrics
     },
     {
+      path: '/metrics/relation',
+      name: 'ServiceRelation',
+      component: ServiceRelation
+    },
+    {
       path: '/management',
       name: 'Management',
       component: Management