Feat instance dependency (#233)

* feat: add popup for instance dependency

* feat: click dependency instance

* refactor: add detect point component

* feat: add instance detect point

* feat: add instance dependency query for metric

* feat: update Instance Dependency

* feat: add modes

* feat: add dependency sankey

* fix: rm dead code

* fix: dialog rerender

* feat: update modules

* feat: update cpm metric
diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts
index af107f5..96fae7d 100644
--- a/src/assets/lang/en.ts
+++ b/src/assets/lang/en.ts
@@ -104,6 +104,8 @@
   nextService: 'Next Service',
   object: 'Object',
   metrics: 'Metrics',
+  ShowInstanceDependency: 'Show Instance Dependency',
+  InstanceDependencyTitle: 'Service Instance Dependency',
 };
 
 export default m;
diff --git a/src/assets/lang/zh.ts b/src/assets/lang/zh.ts
index 91a64fd..5b455ca 100644
--- a/src/assets/lang/zh.ts
+++ b/src/assets/lang/zh.ts
@@ -104,6 +104,8 @@
   nextService: '下一个服务',
   object: '粒度',
   metrics: '指标',
+  ShowInstanceDependency: '展示实例依赖',
+  InstanceDependencyTitle: '实例依赖',
 };
 
 export default m;
diff --git a/src/components/rk-echarts.vue b/src/components/rk-echarts.vue
index c7daecf..74f7b13 100644
--- a/src/components/rk-echarts.vue
+++ b/src/components/rk-echarts.vue
@@ -27,6 +27,7 @@
 @Component
 export default class RkEcharts extends Vue {
   @Prop() private option: any;
+  @Prop() private clickEvent: any;
   @Prop({ default: false }) private uncombine!: boolean;
   @Prop({ default: '100%' }) private height!: string;
   @Prop({default: '100%' }) private width!: string;
@@ -59,6 +60,9 @@
     const el: any = this.$el;
     this.myChart = echarts.init(el, '');
     this.myChart.setOption(this.option);
+    this.myChart.on('click', (params: any) => {
+      this.clickEvent(params);
+    });
   }
 }
 </script>
diff --git a/src/event-bus.ts b/src/event-bus.ts
index a8e0068..99a098d 100644
--- a/src/event-bus.ts
+++ b/src/event-bus.ts
@@ -1,3 +1,20 @@
+/**
+ * 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.
+ */
+
 import Vue, { VueConstructor  } from 'vue';
 
 type VueComponentVM = Vue & { _uid: string; };
diff --git a/src/graph/fragments/topology.ts b/src/graph/fragments/topology.ts
index 0f7b8a6..8a5dd0f 100644
--- a/src/graph/fragments/topology.ts
+++ b/src/graph/fragments/topology.ts
@@ -153,6 +153,166 @@
     }
 `};
 
+export const TopoInstanceClientInfo = {
+  variable: '$duration: Duration!, $id: ID!',
+  query: `
+    getResponseTimeTrend: getLinearIntValues(metric: {
+      name: "service_instance_relation_client_resp_time"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    getThroughputTrend: getLinearIntValues(metric: {
+      name: "service_instance_relation_client_cpm"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    getSLATrend: getLinearIntValues(metric: {
+      name: "service_instance_relation_client_call_sla"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p50: getLinearIntValues(metric: {
+      name: "service_instance_relation_client_p50"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p75: getLinearIntValues(metric: {
+      name: "service_instance_relation_client_p75"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p90: getLinearIntValues(metric: {
+      name: "service_instance_relation_client_p90"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p95: getLinearIntValues(metric: {
+      name: "service_instance_relation_client_p95"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p99: getLinearIntValues(metric: {
+      name: "service_instance_relation_client_p99"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+`};
+
+export const TopoInstanceServerInfo = {
+  variable: '$duration: Duration!, $id: ID!',
+  query: `
+    getResponseTimeTrend: getLinearIntValues(metric: {
+      name: "service_instance_relation_server_resp_time"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    getThroughputTrend: getLinearIntValues(metric: {
+      name: "service_instance_relation_server_cpm"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    getSLATrend: getLinearIntValues(metric: {
+      name: "service_instance_relation_server_call_sla"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p50: getLinearIntValues(metric: {
+      name: "service_instance_relation_server_p50"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p75: getLinearIntValues(metric: {
+      name: "service_instance_relation_server_p75"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p90: getLinearIntValues(metric: {
+      name: "service_instance_relation_server_p90"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p95: getLinearIntValues(metric: {
+      name: "service_instance_relation_server_p95"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+    p99: getLinearIntValues(metric: {
+      name: "service_instance_relation_server_p99"
+      id: $id
+    }, duration: $duration) {
+      values {
+        value
+      }
+    }
+`};
+
+export const TopoInstanceDependency = {
+  variable: '$clientServiceId: ID!, $serverServiceId: ID!, $duration: Duration!',
+  query: `
+  topo: getServiceInstanceTopology(clientServiceId: $clientServiceId,
+    serverServiceId: $serverServiceId, duration: $duration) {
+    nodes {
+      id
+      name
+      type
+      isReal
+      serviceName
+      serviceId
+    }
+    calls {
+      id
+      source
+      detectPoints
+      target
+    }
+  }
+`};
+
 export const Topo = {
   variable: '$duration: Duration!',
   query: `
@@ -261,3 +421,28 @@
       value
     }
   }`};
+
+export const DependencyInstanceServerMetric = {
+  variable: '$duration: Duration!, $idsC: [ID!]!',
+  query: `
+  cpmC: getValues(metric: {
+    name: "service_instance_relation_server_cpm"
+    ids: $idsC
+  }, duration: $duration) {
+    values {
+      id
+      value
+    }
+  }`};
+export const DependencyInstanceClientMetric = {
+  variable: '$duration: Duration!, $idsC: [ID!]!',
+  query: `
+  cpmC: getValues(metric: {
+    name: "service_instance_relation_client_cpm"
+    ids: $idsC
+  }, duration: $duration) {
+    values {
+      id
+      value
+    }
+  }`};
diff --git a/src/graph/query/topology.ts b/src/graph/query/topology.ts
index efce939..0588b70 100644
--- a/src/graph/query/topology.ts
+++ b/src/graph/query/topology.ts
@@ -15,8 +15,9 @@
  * limitations under the License.
  */
 
-import { Topo, ServiceTopo, TopoMetric,
-  TopoServiceMetric, TopoClientMetric, TopoServiceInfo, TopoClientInfo } from '../fragments/topology';
+import { Topo, ServiceTopo, TopoMetric, TopoInstanceDependency, TopoInstanceClientInfo, TopoInstanceServerInfo,
+  TopoServiceMetric, TopoClientMetric, TopoServiceInfo, TopoClientInfo,
+  DependencyInstanceServerMetric, DependencyInstanceClientMetric } from '../fragments/topology';
 
 export const queryTopo =
   `query queryTopo(${Topo.variable}) {${Topo.query}}`;
@@ -41,3 +42,18 @@
 
 export const queryTopoClientInfo =
   `query queryTopoClientInfo(${TopoClientInfo.variable}) {${TopoClientInfo.query}}`;
+
+export const queryTopoInstanceDependency =
+  `query queryTopoInstanceDependency(${TopoInstanceDependency.variable}) {${TopoInstanceDependency.query}}`;
+
+export const queryTopoInstanceServerInfo =
+  `query queryTopoInstanceServerInfo(${TopoInstanceServerInfo.variable}) {${TopoInstanceServerInfo.query}}`;
+
+export const queryTopoInstanceClientInfo =
+  `query queryTopoInstanceClientInfo(${TopoInstanceClientInfo.variable}) {${TopoInstanceClientInfo.query}}`;
+
+export const queryDependencyInstanceServerMetric =
+  `query queryDependencyInstanceServerMetric(${DependencyInstanceServerMetric.variable}) {${DependencyInstanceServerMetric.query}}`;
+
+export const queryDependencyInstanceClientMetric =
+  `query queryDependencyInstanceClientMetric(${DependencyInstanceClientMetric.variable}) {${DependencyInstanceClientMetric.query}}`;
diff --git a/src/store/modules/topology/index.ts b/src/store/modules/topology/index.ts
index 0cf662c..2c9e25d 100644
--- a/src/store/modules/topology/index.ts
+++ b/src/store/modules/topology/index.ts
@@ -19,6 +19,7 @@
 import graph from '@/graph';
 import * as types from '../../mutation-types';
 import { AxiosResponse } from 'axios';
+
 interface Option {
   key: string;
   label: string;
@@ -27,8 +28,10 @@
   avgResponseTime: number;
   cpm: number;
   isAlert: boolean;
-  source: string;
-  target: string;
+  source: string | any;
+  target: string | any;
+  id: string;
+  detectPoints: string[];
 }
 interface Node {
   apdex: number;
@@ -49,7 +52,7 @@
   calls: Call[];
   nodes: Node[];
   detectPoints: string[];
-  selectedCallId: string;
+  selectedServiceCall: Call | null;
   currentNode: any;
   current: Option;
   mode: boolean;
@@ -66,13 +69,20 @@
   showTraceDialog: boolean;
   showInstancesDialog: boolean;
   showEndpointDialog: boolean;
+  instanceDependency: {
+    calls: Call[];
+    nodes: Node[];
+  };
+  selectedInstanceCall: Call | null;
+  instanceDependencyMetrics: {[key: string]: any};
+  queryInstanceMetricsType: string;
 }
 
 const initState: State = {
   callback: '',
   mode: true,
   detectPoints: [],
-  selectedCallId: '',
+  selectedServiceCall: null,
   calls: [],
   nodes: [],
   currentNode: {},
@@ -93,6 +103,13 @@
   showTraceDialog: false,
   showInstancesDialog: false,
   showEndpointDialog: false,
+  instanceDependency: {
+    calls: [],
+    nodes: [],
+  },
+  selectedInstanceCall: null,
+  instanceDependencyMetrics: {},
+  queryInstanceMetricsType: '',
 };
 
 // getters
@@ -135,8 +152,10 @@
     state.calls = data.calls;
     state.nodes = data.nodes;
   },
+  [types.SET_SELECTED_CALL](state: State, data: any) {
+    state.selectedServiceCall = data;
+  },
   [types.SET_TOPO_RELATION](state: State, data: any) {
-    state.selectedCallId = data.id;
     state.getResponseTimeTrend = data.getResponseTimeTrend ?
     data.getResponseTimeTrend.values.map((i: any) => i.value) : [];
     state.getSLATrend = data.getSLATrend ? data.getSLATrend.values.map((i: any) => i.value) : [];
@@ -147,6 +166,28 @@
     state.p95 = data.p95 ? data.p95.values.map((i: any) => i.value) : [];
     state.p99 = data.p99 ? data.p99.values.map((i: any) => i.value) : [];
   },
+  [types.SET_INSTANCE_DEPENDENCY](state: State, data: any) {
+    state.instanceDependency = data;
+  },
+  [types.SET_SELECTED_INSTANCE_CALL](state: State, data: Call) {
+    state.selectedInstanceCall = data;
+  },
+  [types.SET_INSTANCE_DEPEDENCE_METRICS](state: State, data: any) {
+    state.instanceDependencyMetrics.getResponseTimeTrend = data.getResponseTimeTrend ?
+    data.getResponseTimeTrend.values.map((i: any) => i.value) : [];
+    state.instanceDependencyMetrics.getSLATrend = data.getSLATrend ?
+    data.getSLATrend.values.map((i: any) => i.value) : [];
+    state.instanceDependencyMetrics.getThroughputTrend = data.getThroughputTrend ?
+    data.getThroughputTrend.values.map((i: any) => i.value) : [];
+    state.instanceDependencyMetrics.p50 = data.p50 ? data.p50.values.map((i: any) => i.value) : [];
+    state.instanceDependencyMetrics.p75 = data.p75 ? data.p75.values.map((i: any) => i.value) : [];
+    state.instanceDependencyMetrics.p90 = data.p90 ? data.p90.values.map((i: any) => i.value) : [];
+    state.instanceDependencyMetrics.p95 = data.p95 ? data.p95.values.map((i: any) => i.value) : [];
+    state.instanceDependencyMetrics.p99 = data.p99 ? data.p99.values.map((i: any) => i.value) : [];
+  },
+  [types.SET_INSTANCE_DEPEDENCE_TYPE](state: State, data: string) {
+    state.queryInstanceMetricsType = data;
+  },
 };
 
 // actions
@@ -156,13 +197,30 @@
   },
   CLEAR_TOPO_INFO(context: { commit: Commit; state: State; }) {
     context.commit(types.SET_TOPO_RELATION, {});
+    context.commit(types.SET_SELECTED_CALL, null);
+  },
+  GET_INSTANCE_DEPENDENCY_METRICS(
+    context: { commit: Commit; state: State, dispatch: Dispatch, getters: any}, params: any,
+  ) {
+    if (params.mode === 'SERVER') {
+      params.queryType = 'queryTopoInstanceServerInfo';
+      context.dispatch('INSTANCE_RELATION_INFO', params);
+    }
+    if (params.mode === 'CLIENT') {
+      params.queryType = 'queryTopoInstanceClientInfo';
+      context.dispatch('INSTANCE_RELATION_INFO', params);
+    }
   },
   GET_TOPO_SERVICE_INFO(context: { commit: Commit; state: State; }, params: any) {
     return graph
     .query('queryTopoServiceInfo')
-    .params(params)
+    .params({
+      id: params.id,
+      duration: params.duration,
+    })
     .then((res: AxiosResponse) => {
-      context.commit('SET_TOPO_RELATION', Object.assign(res.data.data, { id: params.id }));
+      context.commit('SET_TOPO_RELATION', res.data.data);
+      context.commit(types.SET_SELECTED_CALL, params);
     });
   },
   GET_TOPO_CLIENT_INFO(context: { commit: Commit; state: State; }, params: any) {
@@ -170,7 +228,8 @@
     .query('queryTopoClientInfo')
     .params(params)
     .then((res: AxiosResponse) => {
-      context.commit('SET_TOPO_RELATION', Object.assign(res.data.data, { id: params.id }));
+      context.commit('SET_TOPO_RELATION', res.data.data);
+      context.commit(types.SET_SELECTED_CALL, params);
     });
   },
   GET_TOPO(context: { commit: Commit; state: State; }, params: any) {
@@ -244,6 +303,77 @@
         });
     });
   },
+  async GET_TOPO_INSTANCE_DEPENDENCY(context: { commit: Commit; state: State; }, params: {
+    clientServiceId: string, serverServiceId: string, duration: string}) {
+
+    graph.query('queryTopoInstanceDependency').params(params)
+      .then((res: AxiosResponse) => {
+        if (!(res.data && res.data.data)) {
+          return;
+        }
+        const serverIdsC = [] as string[];
+        const clientIdsC = [] as string[];
+        const topoCalls = res.data.data.topo.calls;
+        for (const call of topoCalls) {
+          if (call.detectPoints.includes('CLIENT')) {
+            clientIdsC.push(call.id);
+          } else {
+            serverIdsC.push(call.id);
+          }
+        }
+        graph.query('queryDependencyInstanceClientMetric').params({
+          idsC: clientIdsC,
+          duration: params.duration,
+        }).then((json: AxiosResponse) => {
+          const clientCalls = [] as string[];
+          for (const call of topoCalls) {
+            for (const cpm of json.data.data.cpmC.values) {
+              if (cpm.id === call.id) {
+                clientCalls.push({
+                  ...call,
+                  ...cpm,
+                });
+              }
+            }
+          }
+          graph.query('queryDependencyInstanceServerMetric').params({
+            idsC: serverIdsC,
+            duration: params.duration,
+          }).then((jsonResp: AxiosResponse) => {
+            const serverCalls = [] as string[];
+            for (const call of topoCalls) {
+              for (const cpm of jsonResp.data.data.cpmC.values) {
+                if (cpm.id === call.id) {
+                  serverCalls.push({
+                    ...call,
+                    ...cpm,
+                  });
+                }
+              }
+            }
+            const data = {
+              nodes: res.data.data.topo.nodes,
+              calls: [...serverCalls, ...clientCalls],
+            };
+            context.commit(types.SET_INSTANCE_DEPENDENCY, data);
+          });
+        });
+      });
+  },
+  INSTANCE_RELATION_INFO(context: { commit: Commit; state: State; }, params: Call &
+    {mode: string; queryType: string; durationTime: string}) {
+    graph.query(params.queryType).params({
+      id: params.id,
+      duration: params.durationTime,
+    }).then((res: AxiosResponse) => {
+      if (!(res.data && res.data.data)) {
+        return;
+      }
+      context.commit(types.SET_SELECTED_INSTANCE_CALL, params);
+      context.commit(types.SET_INSTANCE_DEPEDENCE_TYPE, params.mode);
+      context.commit(types.SET_INSTANCE_DEPEDENCE_METRICS, res.data.data);
+    });
+  },
 };
 
 export default {
diff --git a/src/store/mutation-types.ts b/src/store/mutation-types.ts
index 889b13f..39b76fa 100644
--- a/src/store/mutation-types.ts
+++ b/src/store/mutation-types.ts
@@ -84,6 +84,7 @@
 export const SET_SHOW_TRACE_DIALOG = 'SET_SHOW_TRACE_DIALOG';
 export const SET_SHOW_INSTANCES_DIALOG = 'SET_SHOW_INSTANCES_DIALOG';
 export const SET_SHOW_ENDPOINT_DIALOG = 'SET_SHOW_ENDPOINT_DIALOG';
+export const SET_INSTANCE_DEPENDENCY = 'SET_INSTANCE_DEPENDENCY';
 
 // comparison
 export const SET_CHARTVAL = 'SET_CHARTVAL';
@@ -97,3 +98,7 @@
 export const SET_CONFIG = 'SET_CONFIG';
 export const SET_SERVICE_TOPOLOGY = 'GET_SERVICE_TOPOLOGY';
 export const SET_METRICS = 'SET_METRICS';
+export const SET_SELECTED_INSTANCE_CALL = 'SET_SELECTED_INSTANCE_CALL';
+export const SET_INSTANCE_DEPEDENCE_METRICS = 'SET_INSTANCE_DEPEDENCE_METRICS';
+export const SET_INSTANCE_DEPEDENCE_TYPE = 'SET_INSTANCE_DEPEDENCE_TYPE';
+export const SET_SELECTED_CALL = 'SET_SELECTED_CALL';
diff --git a/src/views/components/topology/dependency-sankey.vue b/src/views/components/topology/dependency-sankey.vue
new file mode 100644
index 0000000..239853d
--- /dev/null
+++ b/src/views/components/topology/dependency-sankey.vue
@@ -0,0 +1,81 @@
+/**
+ * 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>
+  <RkEcharts height="100%" :option="optionConfigs" :clickEvent="clickLinks"/>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import { Component, Prop } from 'vue-property-decorator';
+import { State, Action, Getter, Mutation } from 'vuex-class';
+import echarts from 'echarts/lib/echarts';
+
+@Component
+export default class DependencySankey extends Vue {
+  @Prop() private data: any;
+  @Getter('durationTime') private durationTime: any;
+  @Action('rocketTopo/GET_INSTANCE_DEPENDENCY_METRICS') private GET_INSTANCE_DEPENDENCY_METRICS: any;
+
+  get optionConfigs() {
+    return {
+      series: {
+        type: 'sankey',
+        focusNodeAdjacency: 'allEdges',
+        data: this.data.nodes,
+        links: this.data.calls,
+        label: {
+          color: '#fff',
+          formatter: ((param: any) => param.data.name),
+        },
+        color: [
+            '#bf99f8',
+            '#3fe1da',
+            '#6be6c1',
+            '#3fcfdc',
+            '#626c91',
+            '#3fbcde',
+            '#a0a7e6',
+            '#3fa9e1',
+            '#96dee8',
+          ],
+        itemStyle: {
+          normal: {
+            borderWidth: 0,
+          },
+        },
+        lineStyle: {
+          normal: {
+            color: 'source',
+            opacity: 0.12,
+          },
+        },
+      },
+    };
+  }
+
+  private clickLinks(params: any) {
+    if (params.dataType === 'edge' && params.data) {
+      this.GET_INSTANCE_DEPENDENCY_METRICS({
+        ...params.data,
+        durationTime: this.durationTime,
+        mode: params.data.detectPoints[0],
+      });
+    }
+  }
+}
+</script>
diff --git a/src/views/components/topology/topo-aside.vue b/src/views/components/topology/topo-aside.vue
index b7ffc0b..b67a246 100644
--- a/src/views/components/topology/topo-aside.vue
+++ b/src/views/components/topology/topo-aside.vue
@@ -22,13 +22,13 @@
       <use xlink:href="#issues"/>
     </svg>
     <svg v-if="showServerInfo" class="link-topo-aside-btn icon cp lg"
-         @click="show = !show"
-         :style="`position:absolute;left:290px;transform: rotate(${show?0 : 180}deg);top:50px;`">
+      @click="show = !show"
+      :style="`position:absolute;left:290px;transform: rotate(${show?0 : 180}deg);top:50px;`">
       <use xlink:href="#chevron-left"/>
     </svg>
     <TopoService/>
     <div v-if="show">
-      <div class="link-topo-aside-box" style="top:50px" v-if="!stateTopo.selectedCallId && showServerInfo">
+      <div class="link-topo-aside-box" style="top:50px" v-if="!stateTopo.selectedServiceCall && showServerInfo">
         <div class="mb-20">
           <span class="b dib mr-20">{{$t('serviceDetail')}}</span>
         </div>
@@ -42,104 +42,23 @@
         </div>
         <div>
           <TopoChart
-              v-if="rocketDashboard.serviceApdexScore.ApdexScore.length"
-              :data="rocketDashboard.serviceApdexScore.ApdexScore"
-              :intervalTime="intervalTime"
-              title="Service ApdexScore"
-              unit=""
+            v-if="rocketDashboard.serviceApdexScore.ApdexScore.length"
+            :data="rocketDashboard.serviceApdexScore.ApdexScore"
+            :intervalTime="intervalTime"
+            title="Service ApdexScore"
+            unit=""
           />
           <TopoChart
-              v-if="rocketDashboard.serviceSLA.SLA.length"
-              :data="rocketDashboard.serviceSLA.SLA"
-              :intervalTime="intervalTime"
-              :precent="true"
-              title="Service SLA"
-              unit="%"
+            v-if="rocketDashboard.serviceSLA.SLA.length"
+            :data="rocketDashboard.serviceSLA.SLA"
+            :intervalTime="intervalTime"
+            :precent="true"
+            title="Service SLA"
+            unit="%"
           />
         </div>
       </div>
-      <div v-if="stateTopo.selectedCallId || showServerInfo"
-           :class="`link-topo-aside-box link-topo-aside-box-${isMini?'min':'max'}`"
-           :style="`top:80px;position: fixed;right: 30px;${showInfoCount === 0 ? 'animation: unset;': ''}`">
-        <svg
-            :style="`position:absolute;left:-48px;top:0;transform: rotate(${isMini?0 : 180}deg);`"
-            class="link-topo-aside-btn icon cp lg"
-            @click="setShowInfo"
-        >
-          <use xlink:href="#chevron-left"/>
-        </svg>
-        <div class="mb-5 clear">
-          <span v-if="stateTopo.selectedCallId" class="b dib mr-20 vm">{{ $t('detectPoint') }}</span>
-          <span v-else-if="showServerInfo" class="b dib mr-20 vm">{{ $t('serviceDetail') }}</span>
-          <span
-              v-if="stateTopo.detectPoints.indexOf('CLIENT') !== -1"
-              :class="{'active':!stateTopo.mode}"
-              class="link-topo-aside-box-btn tc r sm cp b"
-              @click="setMode(false)"
-          >{{ this.$t('client') }}</span>
-          <span
-              v-if="stateTopo.detectPoints.indexOf('SERVER') !== -1"
-              :class="{'active':stateTopo.mode}"
-              class="link-topo-aside-box-btn tc r sm cp b"
-              @click="setMode(true)"
-          >{{ this.$t('server') }}</span>
-        </div>
-        <div v-if="showInfo">
-          <div v-if="stateTopo.selectedCallId">
-            <TopoChart
-                v-if="stateTopo.getResponseTimeTrend.length"
-                :data="stateTopo.getResponseTimeTrend"
-                :intervalTime="intervalTime"
-                :title="$t('avgResponseTime')"
-                unit="ms"
-            />
-            <TopoChart
-                v-if="stateTopo.getThroughputTrend.length"
-                :data="stateTopo.getThroughputTrend"
-                :intervalTime="intervalTime"
-                :title="$t('avgThroughput')"
-                unit="cpm"
-            />
-            <TopoChart
-                v-if="stateTopo.getSLATrend.length"
-                :data="stateTopo.getSLATrend"
-                :intervalTime="intervalTime"
-                :precent="true"
-                :title="$t('avgSLA')"
-                unit="%"
-            />
-            <ChartResponse
-                v-if="stateTopo.p50.length"
-                :data="stateTopo"
-                :intervalTime="intervalTime"
-                :title="$t('percentResponse')"
-            />
-          </div>
-          <div v-else-if="showServerInfo">
-            <TopoChart
-                v-if="rocketDashboard.serviceResponseTime.ResponseTime.length"
-                :data="rocketDashboard.serviceResponseTime.ResponseTime"
-                :intervalTime="intervalTime"
-                title="Service ResponseTime"
-                unit="ms"
-            />
-            <TopoChart
-                v-if="rocketDashboard.serviceThroughput.Throughput.length"
-                :data="rocketDashboard.serviceThroughput.Throughput"
-                :intervalTime="intervalTime"
-                title="Service Throughput"
-                unit="cpm"
-            />
-            <ChartResponse
-                v-if="rocketDashboard.servicePercent.p50.length"
-                :data="rocketDashboard.servicePercent"
-                :intervalTime="intervalTime"
-                title="Service Response Time Percentile"
-                unit="ms"
-            />
-          </div>
-        </div>
-      </div>
+      <TopoDetectPoint />
     </div>
     <el-drawer
         v-if="stateTopo.showAlarmDialog"
@@ -150,9 +69,9 @@
         :modal-append-to-body="false"
     >
       <alarm-containers :style="`height: ${drawerMainBodyHeight}`"
-                        :alarmScope="{label: 'Service', key: 'Service'}"
-                        inTopo
-                        :keyword="stateTopo.honeycombNode.name"
+        :alarmScope="{label: 'Service', key: 'Service'}"
+        inTopo
+        :keyword="stateTopo.honeycombNode.name"
       />
     </el-drawer>
     <el-drawer
@@ -164,19 +83,19 @@
         :modal-append-to-body="false"
     >
       <trace-containers :style="`height: ${drawerMainBodyHeight}`"
-                        :service="{label: stateTopo.honeycombNode.name, key: stateTopo.honeycombNode.id}"
-                        inTopo
+        :service="{label: stateTopo.honeycombNode.name, key: stateTopo.honeycombNode.id}"
+        inTopo
       />
     </el-drawer>
     <instances-survey-window
-        v-if="stateTopo.showInstancesDialog"
-        :is-show.sync="stateTopo.showInstancesDialog"
-        :instances="stateDashboardOption.instances"
+      v-if="stateTopo.showInstancesDialog"
+      :is-show.sync="stateTopo.showInstancesDialog"
+      :instances="stateDashboardOption.instances"
     />
     <endpoint-survey-window
-        v-if="stateTopo.showEndpointDialog"
-        :is-show.sync="stateTopo.showEndpointDialog"
-        :endpoints="stateDashboardOption.endpoints"
+      v-if="stateTopo.showEndpointDialog"
+      :is-show.sync="stateTopo.showEndpointDialog"
+      :endpoints="stateDashboardOption.endpoints"
     />
   </aside>
 </template>
@@ -193,11 +112,14 @@
   import TopoChart from './topo-chart.vue';
   import ChartResponse from './topo-response.vue';
   import TopoService from './topo-services.vue';
+  import TopoInstanceDependency from './topo-instance-dependency.vue';
+  import TopoDetectPoint from './topo-detect-point.vue';
 
   @Component({
     components: {
       TopoChart, TopoService, ChartResponse, Radial, AlarmContainers,
       TraceContainers, InstancesSurveyWindow, EndpointSurveyWindow,
+      TopoInstanceDependency, TopoDetectPoint,
     },
   })
   export default class TopoAside extends Vue {
@@ -208,23 +130,17 @@
     @Action('rocketTopo/GET_TOPO') private GET_TOPO: any;
     @Action('rocketTopo/CLEAR_TOPO') private CLEAR_TOPO: any;
     @Action('SELECT_SERVICE') private SELECT_SERVICE: any;
-    @Action('GET_QUERY') private GET_QUERY: any;
-    @Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
-    @Action('MIXHANDLE_GET_OPTION') private MIXHANDLE_GET_OPTION: any;
     @Mutation('rocketTopo/SET_MODE_STATUS') private SET_MODE_STATUS: any;
     @Action('rocketTopo/CLEAR_TOPO_INFO') private CLEAR_TOPO_INFO: any;
     @Mutation('SET_COMPS_TREE') private SET_COMPS_TREE: any;
     @Mutation('SET_EVENTS') private SET_EVENTS: any;
     @State('rocketDashboard') private rocketDashboard: any;
 
-
+    private dialogTopoVisible = false;
     private drawerMainBodyHeight = '100%';
     private initState = initState;
     private radioStatus: boolean = false;
     private show: boolean = true;
-    private showInfo: boolean = false;
-    private isMini: boolean = true;
-    private showInfoCount: number = 0;
 
     private get showServerInfo() {
       return this.stateTopo.currentNode.name && this.stateTopo.currentNode.isReal;
@@ -272,55 +188,9 @@
       this.getTopo();
     }
 
-    @Watch('stateTopo.selectedCallId')
-    private watchDetectPointNodeId(newValue: string) {
-      if (newValue || this.stateTopo.currentNode.isReal) {
-        this.showInfo = true;
-      } else {
-        this.showInfo = false;
-        this.isMini = true;
-      }
-    }
-
-    @Watch('stateTopo.currentNode.name')
-    private watchCurrentNodeIsReal(newValue: boolean) {
-      const service = this.stateTopo.currentNode;
-      if (this.stateTopo.currentNode.isReal) {
-        this.MIXHANDLE_CHANGE_GROUP_WITH_CURRENT({index: 0, current: 1});
-        this.MIXHANDLE_GET_OPTION({compType: 'service', duration: this.durationTime})
-          .then(() => {
-            this.GET_QUERY({
-              serviceId: service.id || '',
-              duration: this.durationTime,
-            });
-          });
-      }
-      if (newValue || this.stateTopo.selectedCallId) {
-        this.showInfo = true;
-      } else {
-        this.showInfo = false;
-        this.isMini = true;
-      }
-    }
-
-
     private showRadial() {
       this.radioStatus = !this.radioStatus;
     }
-
-    private setMode(mode: boolean) {
-      this.SET_MODE_STATUS(mode);
-      this.stateTopo.callback();
-    }
-
-    private setShowInfo() {
-      this.showInfo = false;
-      this.showInfoCount = 1;
-      this.isMini = !this.isMini;
-      setTimeout(() => {
-        this.showInfo = true;
-      }, 550);
-    }
   }
 </script>
 <style lang="scss">
@@ -337,18 +207,6 @@
     }
   }
 
-  .link-topo-aside-box-btn {
-    color: #626977;
-    border: 1px solid;
-    padding: 0px 3px;
-    width: 45px;
-    display: inline-block;
-
-    &.active {
-      color: #448dfe;
-    }
-  }
-
   .link-topo-aside-btn {
     display: block;
     background: #252a2f9a;
diff --git a/src/views/components/topology/topo-detect-point.vue b/src/views/components/topology/topo-detect-point.vue
new file mode 100644
index 0000000..ab0ac40
--- /dev/null
+++ b/src/views/components/topology/topo-detect-point.vue
@@ -0,0 +1,283 @@
+/**
+* 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>
+  <div v-if="stateTopo.selectedServiceCall || showServerInfo"
+           :class="`link-topo-aside-box link-topo-aside-box-${isMini?'min':'max'}`"
+           :style="`top:80px;position: fixed;right: 30px;${showInfoCount === 0 ? 'animation: unset;': ''}`">
+        <svg
+            :style="`position:absolute;left:-48px;top:0;transform: rotate(${isMini?0 : 180}deg);`"
+            class="link-topo-aside-btn icon cp lg"
+            @click="setShowInfo"
+        >
+          <use xlink:href="#chevron-left"/>
+        </svg>
+        <div class="mb-5 clear">
+          <span v-if="stateTopo.selectedServiceCall" class="b dib mr-20 vm">{{ $t('detectPoint') }}</span>
+          <span v-else-if="showServerInfo" class="b dib mr-20 vm">{{ $t('serviceDetail') }}</span>
+          <span
+              v-if="stateTopo.detectPoints.indexOf('CLIENT') !== -1"
+              :class="{'active':!stateTopo.mode}"
+              class="link-topo-aside-box-btn tc r sm cp b"
+              @click="setMode(false)"
+          >{{ this.$t('client') }}</span>
+          <span
+              v-if="stateTopo.detectPoints.indexOf('SERVER') !== -1"
+              :class="{'active':stateTopo.mode}"
+              class="link-topo-aside-box-btn tc r sm cp b"
+              @click="setMode(true)"
+          >{{ this.$t('server') }}</span>
+        </div>
+        <div v-if="showInfo">
+          <div v-if="stateTopo.selectedServiceCall">
+            <TopoChart
+                v-if="stateTopo.getResponseTimeTrend.length"
+                :data="stateTopo.getResponseTimeTrend"
+                :intervalTime="intervalTime"
+                :title="$t('avgResponseTime')"
+                unit="ms"
+            />
+            <TopoChart
+                v-if="stateTopo.getThroughputTrend.length"
+                :data="stateTopo.getThroughputTrend"
+                :intervalTime="intervalTime"
+                :title="$t('avgThroughput')"
+                unit="cpm"
+            />
+            <TopoChart
+                v-if="stateTopo.getSLATrend.length"
+                :data="stateTopo.getSLATrend"
+                :intervalTime="intervalTime"
+                :precent="true"
+                :title="$t('avgSLA')"
+                unit="%"
+            />
+            <ChartResponse
+                v-if="stateTopo.p50.length"
+                :data="stateTopo"
+                :intervalTime="intervalTime"
+                :title="$t('percentResponse')"
+            />
+          </div>
+          <div v-else-if="showServerInfo">
+            <TopoChart
+                v-if="rocketDashboard.serviceResponseTime.ResponseTime.length"
+                :data="rocketDashboard.serviceResponseTime.ResponseTime"
+                :intervalTime="intervalTime"
+                title="Service ResponseTime"
+                unit="ms"
+            />
+            <TopoChart
+                v-if="rocketDashboard.serviceThroughput.Throughput.length"
+                :data="rocketDashboard.serviceThroughput.Throughput"
+                :intervalTime="intervalTime"
+                title="Service Throughput"
+                unit="cpm"
+            />
+            <ChartResponse
+                v-if="rocketDashboard.servicePercent.p50.length"
+                :data="rocketDashboard.servicePercent"
+                :intervalTime="intervalTime"
+                title="Service Response Time Percentile"
+                unit="ms"
+            />
+          </div>
+        </div>
+        <div class="show-dependency" v-if="stateTopo.selectedServiceCall">
+          <a class="rk-btn lg" @click="openInstanceModal">{{$t('ShowInstanceDependency')}}</a>
+          <el-dialog
+            class="instance-dependency" 
+            :width="'90%'"
+            :title="`${stateTopo.selectedServiceCall.source.name} -> ${stateTopo.selectedServiceCall.target.name} Instance Dependency`"
+            :visible.sync="dialogTopoVisible"
+            :modal-append-to-body="false"
+            :close-on-click-modal="false"
+            :destroy-on-close="true"
+            :before-close="clearInstance"
+          >
+            <TopoInstanceDependency />
+          </el-dialog>
+        </div>
+      </div>
+</template>
+<script lang="ts">
+
+  import { Component, Vue, Watch, Prop } from 'vue-property-decorator';
+  import { Action, Getter, Mutation, State } from 'vuex-class';
+  import topo, { State as topoState } from '@/store/modules/topology';
+  import TopoInstanceDependency from './topo-instance-dependency.vue';
+  import TopoChart from './topo-chart.vue';
+  import ChartResponse from './topo-response.vue';
+
+  @Component({
+    components: {
+      TopoInstanceDependency, ChartResponse, TopoChart,
+    },
+  })
+  export default class TopoDetectPoint extends Vue {
+    @State('rocketTopo') private stateTopo!: topoState;
+    @Getter('intervalTime') private intervalTime: any;
+    @Getter('durationTime') private durationTime: any;
+    @Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
+    @Action('MIXHANDLE_GET_OPTION') private MIXHANDLE_GET_OPTION: any;
+    @Action('GET_QUERY') private GET_QUERY: any;
+    @Mutation('rocketTopo/SET_MODE_STATUS') private SET_MODE_STATUS: any;
+    @State('rocketDashboard') private rocketDashboard: any;
+    @Mutation('rocketTopo/SET_SELECTED_INSTANCE_CALL') private SET_SELECTED_INSTANCE_CALL: any;
+    @Mutation('rocketTopo/SET_INSTANCE_DEPENDENCY') private SET_INSTANCE_DEPENDENCY: any;
+    @Action('rocketTopo/CLEAR_TOPO_INFO') private CLEAR_TOPO_INFO: any;
+    @Action('rocketTopo/GET_TOPO_INSTANCE_DEPENDENCY') private GET_INSTANCE_DEPENDENCY: any;
+
+    private isMini: boolean = true;
+    private showInfoCount: number = 0;
+    private showInfo: boolean = false;
+    private dialogTopoVisible = false;
+
+    private get showServerInfo() {
+      return this.stateTopo.currentNode.name && this.stateTopo.currentNode.isReal;
+    }
+
+    @Watch('stateTopo.selectedServiceCall')
+    private watchDetectPointNodeId(newValue: string) {
+      if (newValue || this.stateTopo.currentNode.isReal) {
+        this.showInfo = true;
+      } else {
+        this.showInfo = false;
+        this.isMini = true;
+      }
+    }
+
+    @Watch('stateTopo.currentNode.name')
+    private watchCurrentNodeIsReal(newValue: boolean) {
+      const service = this.stateTopo.currentNode;
+      if (this.stateTopo.currentNode.isReal) {
+        this.MIXHANDLE_CHANGE_GROUP_WITH_CURRENT({index: 0, current: 1});
+        this.MIXHANDLE_GET_OPTION({compType: 'service', duration: this.durationTime})
+          .then(() => {
+            this.GET_QUERY({
+              serviceId: service.id || '',
+              duration: this.durationTime,
+            });
+          });
+      }
+      if (newValue || this.stateTopo.selectedServiceCall) {
+        this.showInfo = true;
+      } else {
+        this.showInfo = false;
+        this.isMini = true;
+      }
+    }
+
+    private setShowInfo() {
+      this.showInfo = false;
+      this.showInfoCount = 1;
+      this.isMini = !this.isMini;
+      setTimeout(() => {
+        this.showInfo = true;
+      }, 550);
+    }
+
+    private setMode(mode: boolean) {
+      this.SET_MODE_STATUS(mode);
+      this.stateTopo.callback();
+    }
+
+    private clearInstance() {
+      this.dialogTopoVisible = false;
+      this.SET_SELECTED_INSTANCE_CALL(null);
+    }
+
+    private openInstanceModal() {
+      this.dialogTopoVisible = true;
+      if (!(this.stateTopo.selectedServiceCall && this.stateTopo.selectedServiceCall.source)) {
+        return;
+      }
+      this.GET_INSTANCE_DEPENDENCY({
+        serverServiceId: this.stateTopo.selectedServiceCall.source.id,
+        clientServiceId: this.stateTopo.selectedServiceCall.target.id,
+        duration: this.durationTime,
+      });
+    }
+  }
+
+</script>
+<style lang="scss">
+  .link-topo-aside-box-btn {
+    color: #626977;
+    border: 1px solid;
+    padding: 0px 3px;
+    width: 45px;
+    display: inline-block;
+    &.active {
+      color: #448dfe;
+    }
+  }
+  .show-dependency {
+    margin: 20px 0;
+    .rk-btn {
+      display: block;
+      text-align: center;
+    }
+    .el-dialog__header{
+      background: #333;
+      .el-dialog__title{
+        color: #efeff1;
+      }
+    }
+    .el-dialog__body{
+      background: #333;
+      color: #efeff1;
+      height: 650px;
+      padding: 10px 20px;
+    }
+  }
+  .link-topo-aside-box {
+    border-radius: 4px;
+    position: absolute;
+    width: 280px;
+    z-index: 101;
+    color: #ddd;
+    background-color: #333;
+    padding: 15px 20px 10px;
+
+    .label {
+      display: inline-block;
+      width: 40%;
+    }
+
+    .content {
+      vertical-align: top;
+      display: inline-block;
+      width: 60%;
+    }
+
+    .circle {
+      width: 8px;
+      height: 8px;
+      border-radius: 4px;
+      background-color: #EE5B5B;
+      margin-top: 6px;
+    }
+  }
+
+  .link-topo-aside-box {
+    p {
+      margin-block-start: auto !important;
+      margin-block-end: auto !important;
+    }
+  }
+</style>
\ No newline at end of file
diff --git a/src/views/components/topology/topo-instance-dependency.vue b/src/views/components/topology/topo-instance-dependency.vue
new file mode 100644
index 0000000..dc455dd
--- /dev/null
+++ b/src/views/components/topology/topo-instance-dependency.vue
@@ -0,0 +1,129 @@
+/**
+ * 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>
+  <div class="rk-topo-instance-dependency">
+    <div class="rk-dependency-chart">
+      <DependencySankey :data="stateTopo.instanceDependency" />
+    </div>
+    <div v-if="!stateTopo.instanceDependency.nodes.length">No Instance Dependency</div>
+    <div v-if="stateTopo.selectedInstanceCall" class="rk-instance-dependency-metrics">
+      <div class="mb-5 clear">
+        <span class="b dib mr-20 vm">{{ $t('detectPoint') }}</span>
+        <span
+            v-if="stateTopo.selectedInstanceCall.detectPoints.includes('CLIENT')"
+            class="link-topo-aside-box-btn tc r sm cp b"
+            :class="{'active':stateTopo.queryInstanceMetricsType==='CLIENT'}"
+            @click="setMode('CLIENT')"
+        >{{ this.$t('client') }}</span>
+        <span
+            v-if="stateTopo.selectedInstanceCall.detectPoints.includes('SERVER')"
+            class="link-topo-aside-box-btn tc r sm cp b"
+            :class="{'active':stateTopo.queryInstanceMetricsType==='SERVER' }"
+            @click="setMode('SERVER')"
+        >{{ this.$t('server') }}</span>
+      </div>
+      <div v-if="stateTopo.selectedInstanceCall">
+        <TopoChart
+            v-if="stateTopo.instanceDependencyMetrics.getResponseTimeTrend"
+            :data="stateTopo.instanceDependencyMetrics.getResponseTimeTrend"
+            :intervalTime="intervalTime"
+            :title="$t('avgResponseTime')"
+            unit="ms"
+        />
+        <TopoChart
+            v-if="stateTopo.instanceDependencyMetrics.getThroughputTrend"
+            :data="stateTopo.instanceDependencyMetrics.getThroughputTrend"
+            :intervalTime="intervalTime"
+            :title="$t('avgThroughput')"
+            unit="cpm"
+        />
+        <TopoChart
+            v-if="stateTopo.instanceDependencyMetrics.getSLATrend"
+            :data="stateTopo.instanceDependencyMetrics.getSLATrend"
+            :intervalTime="intervalTime"
+            :precent="true"
+            :title="$t('avgSLA')"
+            unit="%"
+        />
+        <ChartResponse
+            v-if="stateTopo.instanceDependencyMetrics.p50"
+            :data="stateTopo.instanceDependencyMetrics"
+            :intervalTime="intervalTime"
+            :title="$t('percentResponse')"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+<script lang="ts">
+import { Vue, Component, Prop } from 'vue-property-decorator';
+import { State, Action, Getter, Mutation } from 'vuex-class';
+
+import { State as topoState} from '@/store/modules/topology';
+import Topo from './topo.vue';
+import TopoChart from './topo-chart.vue';
+import ChartResponse from './topo-response.vue';
+import DependencySankey from './dependency-sankey.vue';
+
+@Component({
+  components: {
+    Topo, ChartResponse, TopoChart, DependencySankey,
+  },
+})
+export default class TopoInstanceDependency extends Vue {
+  @Getter('durationTime') private durationTime: any;
+  @State('rocketTopo') private stateTopo!: topoState;
+  @State('rocketDashboard') private rocketDashboard: any;
+  @Getter('intervalTime') private intervalTime: any;
+  @Mutation('rocketTopo/SET_INSTANCE_DEPEDENCE_TYPE') private SET_MODE_STATUS: any;
+  @Action('rocketTopo/GET_INSTANCE_DEPENDENCY_METRICS') private GET_INSTANCE_DEPENDENCY_METRICS: any;
+
+  private showInfo: boolean = true;
+
+  private setMode(mode: string) {
+    this.GET_INSTANCE_DEPENDENCY_METRICS({
+      ...this.stateTopo.selectedInstanceCall,
+      durationTime: this.durationTime,
+      mode,
+    });
+  }
+}
+
+</script>
+<style lang="scss">
+  .rk-topo-instance-dependency {
+    height: 100%;
+    display: flex;
+    flex-direction: row;
+    &>:first-child {
+      line-height: 400px;
+      text-align: center;
+      width: 100%;
+    }
+    .rk-instance-dependency-metrics {
+      width: 320px;
+      height: 100%;
+      background: #111;
+      padding: 10px 20px;
+    }
+    .rk-dependency-chart {
+      width: 850px;
+      height: 100%;
+    }
+  }
+</style>
diff --git a/src/views/components/topology/topo-services.vue b/src/views/components/topology/topo-services.vue
index 8011746..73d4899 100644
--- a/src/views/components/topology/topo-services.vue
+++ b/src/views/components/topology/topo-services.vue
@@ -16,7 +16,7 @@
 */
 
 <template>
-  <div class="link-topo-aside-box" style="padding:0;z-index:102;">
+  <div class="link-topo-aside-box" style="padding:0;">
     <TopoSelect :current="service" :data="services" @onChoose="handleChange"/>
   </div>
 </template>
diff --git a/src/views/components/topology/topo.vue b/src/views/components/topology/topo.vue
index 2a7fb8f..66b3061 100644
--- a/src/views/components/topology/topo.vue
+++ b/src/views/components/topology/topo.vue
@@ -19,16 +19,16 @@
   <div class="micro-topo-chart"></div>
 </template>
 <script lang="js">
- import CssHelper from '@/utils/cssHelper';
- import * as d3 from 'd3';
- import d3tip from 'd3-tip';
+  import CssHelper from '@/utils/cssHelper';
+  import * as d3 from 'd3';
+  import d3tip from 'd3-tip';
  /* tslint:disable */
-const diagonal = d3.linkHorizontal()
-  .x(function (d) { return d.x })
-  .y(function (d) { return d.y });
-const diagonalvertical = d3.linkVertical()
-  .x(function (d) { return d.x })
-  .y(function (d) { return d.y });
+  const diagonal = d3.linkHorizontal()
+    .x(function (d) { return d.x })
+    .y(function (d) { return d.y });
+  const diagonalvertical = d3.linkVertical()
+    .x(function (d) { return d.x })
+    .y(function (d) { return d.y });
 
 export default {
   props: {
@@ -141,26 +141,26 @@
     'datas.nodes': 'draw',
   },
   methods: {
-   removeHoneycomb(that) {
-    const appGovernTopoHoneycombFrames = d3.select('#app-govern-topo-honeycomb-frames');
-    appGovernTopoHoneycombFrames.nodes().forEach((node) => {
-     const childrenArray = Array.from(node.children).reverse();
-     _.forEach(childrenArray, (ele, index) => {
-      ele.classList.toggle('reverse');
+    removeHoneycomb(that) {
+      const appGovernTopoHoneycombFrames = d3.select('#app-govern-topo-honeycomb-frames');
+      appGovernTopoHoneycombFrames.nodes().forEach((node) => {
+      const childrenArray = Array.from(node.children).reverse();
+      _.forEach(childrenArray, (ele, index) => {
+        ele.classList.toggle('reverse');
+        setTimeout(() => {
+        ele.remove();
+        }, 130 * index);
+      });
+      });
       setTimeout(() => {
-       ele.remove();
-      }, 130 * index);
-     });
-    });
-    setTimeout(() => {
-     appGovernTopoHoneycombFrames.remove();
-    }, 780);
-   },
+      appGovernTopoHoneycombFrames.remove();
+      }, 780);
+    },
     draw(value, oldValue) {
       // Avoid unnecessary repetitive rendering
       const diffNodes = _.difference(_.sortBy(value, 'id'), _.sortBy(oldValue, 'id'));
-      if(value && value.length > 0 && diffNodes && diffNodes.length <=0){
-        return null;
+      if(value && value.length > 0 && diffNodes && diffNodes.length <=0) {
+        return;
       }
       const codeId = this.datas.nodes.map(i => i.id);
       for (let i = 0; i < this.datas.calls.length; i += 1) {
@@ -169,7 +169,7 @@
           this.datas.calls[i].target = this.datas.calls[i].source;
         }
       }
-      this.svg.select('.graph').remove();
+      this.svg.select(`.graph_${this.datas.type || ''}`).remove();
       this.force = d3
         .forceSimulation(this.datas.nodes)
         .force('collide', d3.forceCollide().radius(() => 65))
@@ -180,7 +180,7 @@
         .force('center', d3.forceCenter(window.innerWidth / 2 + 100, this.height / 2))
         .on('tick', this.tick)
         .stop();
-      this.graph = this.svg.append('g').attr('class', 'graph');
+      this.graph = this.svg.append('g').attr('class', `graph_${this.datas.type || ''}`);
       this.svg.call(this.getZoomBehavior(this.graph));
       this.graph.call(this.tip);
       this.graph.call(this.tipName);
@@ -226,57 +226,11 @@
         })
         .on('click', function(d, i) {
           event.stopPropagation();
-          that.tip.hide({}, this);
-          that.node.attr('class', '');
-          d3.select(this).attr('class', 'node-active');
-          const copyD = JSON.parse(JSON.stringify(d));
-          delete copyD.x;
-          delete copyD.y;
-          delete copyD.vx;
-          delete copyD.vy;
-          delete copyD.fx;
-          delete copyD.fy;
-          delete copyD.index;
-          that.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
-          that.$store.commit('rocketTopo/SET_NODE', copyD);
-          that.toggleNode(that.node, d, true);
-          that.toggleLine(that.line, d, true);
-          that.toggleLine(that.lineNode, d, true);
+          // active selected nodes and disable another nodes of non-relations
+          that.clickNodesToUpdate(d, this);
           if (d.isReal) {
-            const honeycombFrames = d3.select('#honeycomb-selector_honeycomb-frames');
-            const appGovernTopoHoneycombFrames = that.graph.append('g')
-              .attr('id', 'app-govern-topo-honeycomb-frames')
-              .attr('style', honeycombFrames.attr('style'))
-              .attr('stroke', honeycombFrames.attr('stroke')).html(honeycombFrames.html())
-              .on('mouseleave', function () {
-                that.removeHoneycomb(that);
-              });
-            const nodeTranslate = CssHelper.translateSerialization(this.getAttribute('transform'));
-            const appGovernTopoHoneycombFramesTranslate = CssHelper.matrixSerialization(honeycombFrames.attr('transform'));
-            appGovernTopoHoneycombFramesTranslate.tx = nodeTranslate.x - 83;
-            appGovernTopoHoneycombFramesTranslate.ty = nodeTranslate.y + 72;
-            appGovernTopoHoneycombFrames.attr('transform', CssHelper.matrixDeserialization(appGovernTopoHoneycombFramesTranslate));
-
-            that.$store.commit('rocketTopo/SET_HONEYCOMB_NODE', d);
-
-            d3.selectAll('#honeycomb-selector_honeycomb-group-top-right').on('click', () => {
-             that.$store.commit('rocketTopo/SET_SHOW_ALARM_DIALOG', true);
-             that.removeHoneycomb(that);
-            });
-            d3.selectAll('#honeycomb-selector_honeycomb-group-below-right').on('click', () => {
-             that.$store.commit('rocketTopo/SET_SHOW_TRACE_DIALOG', true);
-             that.removeHoneycomb(that);
-            });
-            d3.selectAll('#honeycomb-selector_honeycomb-group-below-left').on('click', () => {
-             that.$store.commit('SET_CURRENT_SERVICE', { key: d.id, label: d.name });
-             that.$store.commit('rocketTopo/SET_SHOW_INSTANCES_DIALOG', true);
-             that.removeHoneycomb(that);
-            });
-            d3.selectAll('#honeycomb-selector_honeycomb-group-top-left').on('click', () => {
-             that.$store.commit('SET_CURRENT_SERVICE', { key: d.id, label: d.name });
-             that.$store.commit('rocketTopo/SET_SHOW_ENDPOINT_DIALOG', true);
-             that.removeHoneycomb(that);
-            });
+            // show some entrance icons for service nodes, such as alarm, instance, endpoint
+            that.dashboardEntranceIcons(d, this);
           }
         });
       this.node
@@ -319,7 +273,8 @@
         .attr('text-anchor', 'middle')
         .attr('x', 22)
         .attr('y', 70)
-        .text(d => d.name.length > 12 ? `${d.name.substring(0,12)}...`: d.name)
+        // .text(d => d.name)
+        .text(d => d.name.length > 20 ? `${d.name.substring(0, 20)}...`: d.name)
       this.glink = this.graph.append('g').selectAll('.link');
       this.link = this.glink.data(this.datas.calls).enter();
       this.line = this.link.append('path').attr('class', 'link')
@@ -329,24 +284,13 @@
         that.tip.hide({}, this);
       }
       this.lineNode = this.link.append('rect').attr('class', 'link-node cp')
-        .attr('width', 6)
-        .attr('height', 6)
+        .attr('width', 10)
+        .attr('height', 10)
         .attr('rx', 3)
         .attr('ry', 3)
         .attr('fill', d => d.cpm ? '#217EF299' : '#6a6d7799')
         .on('click', function(d, i) {
-         that.$store.commit('rocketTopo/SET_NODE', {});
-         that.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
-         that.$store.commit('rocketTopo/SET_MODE', d.detectPoints);
-          event.stopPropagation();
-          that.tip.hide({}, this);
-          that.tip.show(d, this);
-          that.$store.dispatch(that.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' : 'rocketTopo/GET_TOPO_CLIENT_INFO', {id:d.id,duration: that.$store.getters.durationTime});
-          that.$store.commit('rocketTopo/SET_CALLBACK', function() {
-            that.tip.hide({}, this);
-            that.tip.show(d, this);
-            that.$store.dispatch(that.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' : 'rocketTopo/GET_TOPO_CLIENT_INFO', {id:d.id,duration: that.$store.getters.durationTime});
-          })
+          that.clickLinkNodes(d, this);
         });
       d3.timeout(() => {
         for (
@@ -362,8 +306,77 @@
           this.tick();
         }
       });
-    },
-    isLinkNode(currNode, node) {
+  },
+  clickLinkNodes(d, that) {
+    this.$store.commit('rocketTopo/SET_NODE', {});
+    this.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
+    this.$store.commit('rocketTopo/SET_MODE', d.detectPoints);
+    event.stopPropagation();
+    this.tip.hide({}, that);
+    this.tip.show(d, that);
+    this.$store.dispatch(this.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' : 'rocketTopo/GET_TOPO_CLIENT_INFO', {...d,duration: this.$store.getters.durationTime});
+    this.$store.commit('rocketTopo/SET_CALLBACK', () => {
+      this.tip.hide({}, that);
+      this.tip.show(d, that);
+      this.$store.dispatch(this.$store.state.rocketTopo.mode ? 'rocketTopo/GET_TOPO_SERVICE_INFO' : 'rocketTopo/GET_TOPO_CLIENT_INFO', {...d,duration: this.$store.getters.durationTime});
+    })
+  },
+  clickNodesToUpdate(d, that) {
+    this.tip.hide({}, that);
+    this.node.attr('class', '');
+    d3.select(that).attr('class', 'node-active');
+    const copyD = JSON.parse(JSON.stringify(d));
+    delete copyD.x;
+    delete copyD.y;
+    delete copyD.vx;
+    delete copyD.vy;
+    delete copyD.fx;
+    delete copyD.fy;
+    delete copyD.index;
+    this.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
+    this.$store.commit('rocketTopo/SET_NODE', copyD);
+    this.toggleNode(this.node, d, true);
+    this.toggleLine(this.line, d, true);
+    this.toggleLine(this.lineNode, d, true);
+  },
+  dashboardEntranceIcons(d, context) {
+    const that = this;
+    const honeycombFrames = d3.select('#honeycomb-selector_honeycomb-frames');
+    const appGovernTopoHoneycombFrames = this.graph.append('g')
+      .attr('id', 'app-govern-topo-honeycomb-frames')
+      .attr('style', honeycombFrames.attr('style'))
+      .attr('stroke', honeycombFrames.attr('stroke')).html(honeycombFrames.html())
+      .on('mouseleave', function () {
+        that.removeHoneycomb(that);
+      });
+    const nodeTranslate = CssHelper.translateSerialization(context.getAttribute('transform'));
+    const appGovernTopoHoneycombFramesTranslate = CssHelper.matrixSerialization(honeycombFrames.attr('transform'));
+    appGovernTopoHoneycombFramesTranslate.tx = nodeTranslate.x - 83;
+    appGovernTopoHoneycombFramesTranslate.ty = nodeTranslate.y + 72;
+    appGovernTopoHoneycombFrames.attr('transform', CssHelper.matrixDeserialization(appGovernTopoHoneycombFramesTranslate));
+
+    that.$store.commit('rocketTopo/SET_HONEYCOMB_NODE', d);
+
+    d3.selectAll('#honeycomb-selector_honeycomb-group-top-right').on('click', () => {
+      that.$store.commit('rocketTopo/SET_SHOW_ALARM_DIALOG', true);
+      that.removeHoneycomb(that);
+    });
+    d3.selectAll('#honeycomb-selector_honeycomb-group-below-right').on('click', () => {
+      this.$store.commit('rocketTopo/SET_SHOW_TRACE_DIALOG', true);
+      that.removeHoneycomb(that);
+    });
+    d3.selectAll('#honeycomb-selector_honeycomb-group-below-left').on('click', () => {
+      that.$store.commit('SET_CURRENT_SERVICE', { key: d.id, label: d.name });
+      that.$store.commit('rocketTopo/SET_SHOW_INSTANCES_DIALOG', true);
+      that.removeHoneycomb(that);
+    });
+    d3.selectAll('#honeycomb-selector_honeycomb-group-top-left').on('click', () => {
+      that.$store.commit('SET_CURRENT_SERVICE', { key: d.id, label: d.name });
+      that.$store.commit('rocketTopo/SET_SHOW_ENDPOINT_DIALOG', true);
+      that.removeHoneycomb(that);
+    });
+  },
+  isLinkNode(currNode, node) {
     if (currNode.id === node.id) {
         return true;
     }
@@ -372,7 +385,7 @@
       (i.source.id === node.id || i.target.id === node.id)
     ).length;
   },
-    toggleNode(nodeCircle, currNode, isHover) {
+  toggleNode(nodeCircle, currNode, isHover) {
     if (isHover) {
       nodeCircle.sort((a, b) => a.id === currNode.id ? 1 : -1);
       nodeCircle