blob: b067132689da4e5d4249b8ff1aa1dee7409569d1 [file] [log] [blame]
/**
* 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="micro-topo-chart"></div>
</template>
<script lang="js">
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 });
export default {
props: {
datas: {
type: Object,
default() {
return {
nodes: [],
calls: [],
};
},
},
},
data() {
return {
LOCAL: require('./assets/Local2.png'),
CUBE: require('./assets/cube22.png'),
CUBEERROR: require('./assets/cube21.png'),
USER: require('./assets/USER.png'),
UNKNOWN: require('./assets/UNKNOWN.png'),
UNKNOWNCLOUD: require('./assets/UNKNOWN_CLOUD.png'),
UNDEFINED: require('./assets/UNDEFINED.png'),
KAFKA: require('./assets/KAFKALOGO.png'),
KAFKACONSUMER: require('./assets/KAFKALOGO.png'),
H2:require('./assets/H2.png'),
REDIS:require('./assets/REDIS.png'),
TOMCAT: require('./assets/TOMCAT.png'),
HTTPCLIENT: require('./assets/HTTPCLIENT.png'),
DUBBO: require('./assets/DUBBO.png'),
MOTAN: require('./assets/MOTAN.png'),
RESIN: require('./assets/RESIN.png'),
OKHTTP: require('./assets/OKHTTP.png'),
SPRINGMVC: require('./assets/SPRINGMVC.png'),
STRUTS2: require('./assets/STRUTS2.png'),
NUTZMVC: require('./assets/SPRINGMVC.png'),
NUTZHTTP: require('./assets/HTTPCLIENT.png'),
JETTY:require('./assets/JETTY.png'),
JETTYSERVER: require('./assets/JETTYSERVER.png'),
GRPC: require('./assets/GRPC.png'),
ORACLE: require('./assets/ORACLE.png'),
MYSQL: require('./assets/MYSQL.png'),
MYSQLGROUP: require('./assets/MYSQL.png'),
MSSQLSERVER: require('./assets/MYSQL.png'),
MSSQLSERVERGROUP: require('./assets/MYSQL.png'),
MONGODB: require('./assets/MONGODB.png'),
MONGODBGROUP: require('./assets/MONGODB.png'),
ACTIVEMQ: require('./assets/ACTIVEMQ.png'),
ELASTICSEARCH: require('./assets/ELASTICSEARCH.png'),
FEIGNDEFAULTHTTP: require('./assets/FEIGNDEFAULTHTTP.png'),
HPROSE: require('./assets/HPROSE.png'),
HPROSE: require('./assets/POSTGRESQL.png'),
RESIN: require('./assets/RESIN.png'),
RABBITMQ: require('./assets/RABBITMQ.png'),
SOFARPC: require('./assets/SOFARPC.png'),
ROCKETMQ: require('./assets/ROCKETMQ.png'),
ROCKETMQCONSUMER: require('./assets/ROCKETMQ.png'),
HTTP: require('./assets/HTTPCLIENT.png'),
RESTEASY: require('./assets/RESTEASY.png'),
SOLR: require('./assets/SOLR.png'),
ZOOKEEPER: require('./assets/ZOOKEEPER.png'),
width: 600,
height: 600,
force: '',
svg: '',
graph: '',
link: '',
node: '',
zoom: '',
};
},
beforeDestroy() {
this.tip.hide({}, this);
window.removeEventListener('resize', this.resize);
d3.selectAll('.d3-tip-grey').remove();
// this.$store.commit('skywalking/setCurrentNode', []);
},
mounted() {
window.addEventListener('resize', this.resize);
this.tip = d3tip()
.attr('class', 'd3-tip-grey')
.offset([-8, 0])
.html(d => {
return `
<div class="mb-5"><span class="grey">${this.$t('cpm')}: </span>${d.cpm}</div>
<div class="mb-5"><span class="grey">${this.$t('detectPoint')}: </span>${this.$store.state.rocketTopo.detectPoints.join(' | ')}</div>
<div><span class="grey">${this.$t('latency')}: </span>${d.latency}</div>
`});
this.tipName = d3tip()
.attr('class', 'd3-tip-grey')
.offset([-8, 0])
.html(d => {
return `<div>${d.name}</div>`});
this.height = this.$el.clientHeight;
this.svg = d3
.select(this.$el)
.append('svg')
.style('display','block')
.attr('width', '100%')
.attr('height', this.height);
},
watch: {
'datas.nodes': 'draw',
},
methods: {
draw() {
const codeId = this.datas.nodes.map(i => i.id);
for (let i = 0; i < this.datas.calls.length; i += 1) {
const element = this.datas.calls[i];
if(codeId.indexOf(element.target) === -1 ) {
this.datas.calls[i].target = this.datas.calls[i].source;
}
}
this.svg.select('.graph').remove();
this.force = d3
.forceSimulation(this.datas.nodes)
.force('collide', d3.forceCollide().radius(() => 65))
.force('yPos', d3.forceY().strength(1))
.force('xPos', d3.forceX().strength(1))
.force('charge', d3.forceManyBody().strength(-520))
.force( 'link', d3.forceLink(this.datas.calls).id(d => d.id))
.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.svg.call(this.getZoomBehavior(this.graph));
this.graph.call(this.tip);
this.graph.call(this.tipName);
this.svg.on('click', (d, i) => {
event.stopPropagation();
event.preventDefault();
this.$store.commit('rocketTopo/SET_NODE', {});
this.$store.dispatch('rocketTopo/CLEAR_TOPO_INFO');
that.tip.hide({}, this);
this.toggleNode(this.node, d, false);
this.toggleLine(this.line, d, false);
this.toggleLine(this.lineNode, d, false);
});
this.defs = this.graph.append('defs');
this.arrowMarker = this.defs
.append('marker')
.attr('id', 'arrow')
.attr('markerUnits', 'strokeWidth')
.attr('markerWidth', '12')
.attr('markerHeight', '12')
.attr('viewBox', '0 0 12 12')
.attr('refX', '11')
.attr('refY', '6')
.attr('orient', 'auto');
const arrow_path = 'M2,2 L10,6 L2,10 L3,6 L2,2';
this.arrowMarker.append('path').attr('d', arrow_path).attr('fill', '#217EF2');
this.gnode = this.graph.append('g').selectAll('.node');
const that = this;
this.node = this.gnode.data(this.datas.nodes)
.enter()
.append('g')
.call(d3.drag()
.on('start', this.dragstart)
.on('drag', this.dragged)
.on('end', function(d, i) {
that.tipName.show(d, this);
}))
.on('mouseover', function(d, i) {
that.tipName.show(d, this);
})
.on('mouseout', function(d, i) {
that.tipName.hide(d, this);
})
.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.commit('rocketTopo/SET_NODE', copyD);
that.toggleNode(that.node, d, true);
that.toggleLine(that.line, d, true);
that.toggleLine(that.lineNode, d, true);
});
this.node
.append('image')
.attr('width', 49)
.attr('height', 49)
.attr('x', 2)
.attr('y', 10)
.attr('style', 'cursor: move;')
.attr('xlink:href',d => {
const type = d.type;
if( d.sla < 98 ) {
return this.CUBEERROR;
}
return this.CUBE;
});
this.node
.append('image')
.attr('width',32)
.attr('height', 32)
.attr('x', 6)
.attr('y', -10)
.attr('style', 'opacity: 0.5;')
.attr('xlink:href',this.LOCAL);
this.node
.append('image')
.attr('width', 18)
.attr('height', 18)
.attr('x', 13)
.attr('y', -7)
.attr('xlink:href',d => {
if( !d.type || d.type === 'N/A') {
return this['UNDEFINED']
}
return this[d.type.toUpperCase().replace('-','')];
});
this.node
.append('text')
.attr('class', 'node-text')
.attr('text-anchor', 'middle')
.attr('x', 22)
.attr('y', 70)
.text(d => d.name.length > 12 ? `${d.name.substring(0,12)}...`: 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')
.attr('stroke-dasharray', '13 7')
.attr('stroke', d => d.cpm ? '#217EF25f' : '#6a6d7777');
const handleSelectLine = function(d, i) {
that.tip.hide({}, this);
}
this.lineNode = this.link.append('rect').attr('class', 'link-node cp')
.attr('width', 6)
.attr('height', 6)
.attr('rx', 3)
.attr('ry', 3)
.attr('fill', d => d.cpm ? '#217EF299' : '#6a6d7799')
.on('click', function(d, i) {
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});
})
});
d3.timeout(() => {
for (
let i = 0,
n = Math.ceil(
Math.log(this.force.alphaMin()) /
Math.log(1 - this.force.alphaDecay())
);
i < n;
i += 1
) {
this.force.tick();
this.tick();
}
});
},
isLinkNode(currNode, node) {
if (currNode.id === node.id) {
return true;
}
return this.datas.calls.filter(i =>
(i.source.id === currNode.id || i.target.id === currNode.id) &&
(i.source.id === node.id || i.target.id === node.id)
).length;
},
toggleNode(nodeCircle, currNode, isHover) {
if (isHover) {
nodeCircle.sort((a, b) => a.id === currNode.id ? 1 : -1);
nodeCircle
.style('opacity', .2)
.filter(node => this.isLinkNode(currNode, node))
.style('opacity', 1);
} else {
nodeCircle.style('opacity', 1);
}
},
toggleLine(linkLine, currNode, isHover) {
if (isHover) {
linkLine
.style('opacity', .05)
.style('animation', 'none')
.filter(link => this.isLinkLine(currNode, link))
.style('opacity', 1)
.style('animation', 'dash 1s linear infinite');
// .classed('link-active', true);
} else {
linkLine
.style('opacity', 1)
.style('animation', 'dash 1s linear infinite');
// .classed('link-active', false);
}
},
isLinkLine(node, link) {
return link.source.id == node.id || link.target.id == node.id;
},
toggleLineText(lineText, currNode, isHover) {
if (isHover) {
lineText
.style('fill-opacity', link => this.isLinkLine(currNode, link) ? 1.0 : 0.0);
} else {
lineText
.style('fill-opacity', '1.0');
}
},
toggleMarker(marker, currNode, isHover) {
if (isHover) {
marker.filter(link => this.isLinkLine(currNode, link))
.style('transform', 'scale(1.5)');
} else {
marker
.attr('refX', nodeConf.radius.Company)
.style('transform', 'scale(1)');
}
},
resize() {
this.svg.attr('height', document.body.clientHeight - 50);
},
tick() {
this.line
.attr('d', d => `M${d.source.x} ${d.source.y} Q ${(d.source.x + d.target.x)/2} ${(d.target.y + d.source.y)/2 - 80} ${d.target.x} ${d.target.y}`);
this.lineNode.attr('transform', d => `translate(${(d.source.x + d.target.x)/2 - 3},${(d.target.y + d.source.y)/2 - 43})`);
// this.linkText.attr('transform',d =>`translate(${(d.source.x + d.target.x) / 2},${(d.source.y + d.target.y) / 2})`);
this.node.attr('transform', d => `translate(${d.x - 22},${d.y - 22})`);
},
getZoomBehavior(g) {
const that = this;
return d3
.zoom()
.scaleExtent([0.3, 10])
.on('zoom', () => {
that.tip.hide({}, this);
that.tipName.hide({}, this);
g.attr(
'transform',
`translate(${d3.event.transform.x},${d3.event.transform.y})scale(${
d3.event.transform.k
})`
);
});
},
dragstart(d) {
const that = this;
that.tipName.hide({}, this);
this.node._groups[0].forEach(d => {
d.__data__.fx = d.__data__.x;
d.__data__.fy = d.__data__.y;
});
if (!d3.event.active) {
this.force.alphaTarget(0.01).restart();
}
d3.event.sourceEvent.stopPropagation();
},
dragged(d) {
const that = this;
that.tipName.hide({}, this);
d.fx = d3.event.x;
d.fy = d3.event.y;
},
dragended() {
if (!d3.event.active) {
this.force.alphaTarget(0);
}
},
},
};
</script>
<style lang="scss">
.micro-topo-chart{
height: 100%;
width: 100%;
.node-name {
cursor: move;
font-size:14px;
fill: #ddd;
}
.link {
stroke-linecap: round;
stroke-width: 1.3px;
fill: none;
animation: dash 1s linear infinite;
}
@keyframes dash {
from {
stroke-dashoffset: 20;
}
to {
stroke-dashoffset: 0;
}
}
.node-text{
font-family: "Lato","Source Han Sans CN", "Microsoft YaHei", sans-serif;
fill: #ddd;
font-size:11px;
opacity: 0.8;
}
.link-text {
font-family: "Lato","Source Han Sans CN", "Microsoft YaHei", sans-serif;
fill: #ddd;
font-size:11px;
opacity: 0.8;
}
.node {
cursor: move;
fill: #333840;
stroke-width: 0;
}
.link-node{
stroke-width: 6px;
stroke: rgba(255, 255, 255, 0);
}
}
</style>