add thread chart and modify cluster-moniter chart style

This commit is contained in:
silenceqi 2020-12-10 21:28:19 +08:00
parent 66f9f0b900
commit f4e6d4e45e
3 changed files with 389 additions and 131 deletions

View File

@ -1,4 +1,4 @@
import { func } from "prop-types"; import fetch from 'node-fetch';
let data = JSON.parse(`{ let data = JSON.parse(`{
"cluster_stats" : { "cluster_stats" : {
@ -478,54 +478,246 @@ let data = JSON.parse(`{
} }
]`); ]`);
const apiUrls = {
CLUSTER_OVERVIEW: {
path:'/.monitoring-es-*/_search',
body: `{
"_source": [ "cluster_stats"],
"size": 1,
"sort": [
{
"timestamp": {
"order": "desc"
}
}
],
"query": {
"bool": {
"must": [
{
"match": {
"type": "cluster_stats"
}
}
],
"filter": [
{
"range": {
"timestamp": {
"gte": "now-1h",
"lte": "now"
}
}
}
]
}
}
}`
},
"GET_ES_NODE_STATS":{
path: '/.monitoring-es-*/_search',
body: `{
"size": 0,
"query": {
"bool": {
"must": [
{
"match": {
"type": "node_stats"
}
}
],
"filter": [
{
"range": {
"timestamp": {
"gte": "now-1h",
"lte": "now"
}
}
}
]
}
},
"aggs": {
"nodes": {
"terms": {
"field": "source_node.name",
"size": 10
},
"aggs": {
"metrics": {
"date_histogram": {
"field": "timestamp",
"fixed_interval": "30s"
},
"aggs": {
"cpu_used": {
"max": {
"field": "node_stats.process.cpu.percent"
}
},
"heap_used": {
"max": {
"field": "node_stats.jvm.mem.heap_used_in_bytes"
}
},
"heap_percent": {
"max": {
"field": "node_stats.jvm.mem.heap_used_percent"
}
},
"search_query_total": {
"max": {
"field": "node_stats.indices.search.query_total"
}
},
"search_query_time": {
"max": {
"field": "node_stats.indices.search.query_time_in_millis"
}
},
"ds": {
"derivative": {
"buckets_path": "search_query_total"
}
},
"ds1": {
"derivative": {
"buckets_path": "search_query_time"
}
},
"index_total": {
"max": {
"field": "node_stats.indices.indexing.index_total"
}
},
"index_time": {
"max": {
"field": "node_stats.indices.indexing.index_time_in_millis"
}
},
"ds3": {
"derivative": {
"buckets_path": "index_total"
}
},
"ds4": {
"derivative": {
"buckets_path": "index_time"
}
},
"read_threads_queue":{
"max": {
"field": "node_stats.thread_pool.get.queue"
}
},
"write_threads_queue":{
"max": {
"field": "node_stats.thread_pool.write.queue"
}
}
}
}
}
}
}
}`
}
};
const gatewayUrl = 'http://localhost:8001';
function getClusterOverview(){
return fetch(gatewayUrl+apiUrls.CLUSTER_OVERVIEW.path, {
method: 'POST',
body: apiUrls.CLUSTER_OVERVIEW.body,
headers:{
'Content-Type': 'application/json'
}
}).then(esRes=>{
return esRes.json();
}).then(rel=>{
if(rel.hits.hits.length>0){
var rdata = rel.hits.hits[0]._source;
}else{
rdata = data;
}
let cluster_stats = rdata.cluster_stats;
let result = {
elasticsearch:{
cluster_stats:{
status: cluster_stats.status,
indices: {
count: cluster_stats.indices.count,
docs: cluster_stats.indices.docs,
shards: cluster_stats.indices.shards,
store: cluster_stats.indices.store,
},
nodes: {
count:{
total: cluster_stats.nodes.count.total,
},
fs: cluster_stats.nodes.fs,
jvm: {
max_uptime_in_millis: cluster_stats.nodes.jvm.max_uptime_in_millis,
mem: cluster_stats.nodes.jvm.mem,
}
}
}
}
};
return result;
});
}
function getNodesStats(){
return fetch(gatewayUrl+apiUrls.GET_ES_NODE_STATS.path, {
method: 'POST',
body: apiUrls.GET_ES_NODE_STATS.body,
headers:{
'Content-Type': 'application/json'
}
}).then(esRes=>{
return esRes.json();
}).then(rel=>{
//console.log(rel);
if(rel.aggregations.nodes.buckets.length>0){
var rdata = rel.aggregations.nodes.buckets;
//console.log(rdata);
}else{
rdata = nodesStats;
}
return rdata;
});
}
export default { export default {
'GET /dashboard/cluster/overview': function(req, res){ 'GET /dashboard/cluster/overview': function(req, res){
let cluster_stats = data.cluster_stats; //console.log(typeof fetch);
let result = { getClusterOverview().then((result)=>{
elasticsearch:{ //console.log(result);
cluster_stats:{ res.send(result);
status: cluster_stats.status, }).catch(err=>{
indices: { console.log(err);
count: cluster_stats.indices.count, });
docs: cluster_stats.indices.docs,
shards: cluster_stats.indices.shards,
store: cluster_stats.indices.store,
},
nodes: {
count:{
total: cluster_stats.nodes.count.total,
},
fs: cluster_stats.nodes.fs,
jvm: {
max_uptime_in_millis: cluster_stats.nodes.jvm.max_uptime_in_millis,
mem: cluster_stats.nodes.jvm.mem,
}
}
}
}
};
res.send(result);
}, },
'GET /dashboard/cluster/nodes_stats': function(req, res) { 'GET /dashboard/cluster/nodes_stats': function(req, res) {
// var statsData = [{ Promise.all([ getNodesStats()]).then((values) => {
// node_name: nodesStats.source_node.name, //console.log(values);
// node_id: nodesStats.source_node.uuid,
// timestamp: nodesStats.timestamp,
// node_stats: {
// jvm: nodesStats.node_stats.jvm,
// process: nodesStats.node_stats.process,
// node_master: nodesStats.node_stats.node_master,
// indices: {
// search: nodesStats.node_stats.indices.search,
// indexing: nodesStats.node_stats.indices.indices,
// },
// fs: nodesStats.node_stats.fs,
// os: nodesStats.node_stats.os,
// }
// }];
res.send({ res.send({
nodes_stats: nodesStats // elasticsearch: values[0].elasticsearch,
nodes_stats: values[0],
}); });
}).catch(err=>{
console.log(err);
});
// getNodesStats().then((rdata)=>{
// res.send({
// nodes_stats: rdata
// });
// }).catch(err=>{
// console.log(err);
// })
}, },
}; };

View File

@ -45,17 +45,22 @@
"repl": "^0.1.3" "repl": "^0.1.3"
}, },
"devDependencies": { "devDependencies": {
"antd-pro-merge-less": "^0.1.0",
"antd-theme-webpack-plugin": "^1.1.8", "antd-theme-webpack-plugin": "^1.1.8",
"autod": "^3.0.1", "autod": "^3.0.1",
"autod-egg": "^1.1.0", "autod-egg": "^1.1.0",
"antd-pro-merge-less": "^0.1.0",
"babel-eslint": "^8.1.2", "babel-eslint": "^8.1.2",
"babel-plugin-dva-hmr": "^0.4.1", "babel-plugin-dva-hmr": "^0.4.1",
"babel-plugin-import": "^1.6.3", "babel-plugin-import": "^1.6.3",
"babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-plugin-transform-decorators-legacy": "^1.3.4",
"egg": "^2.4.1",
"egg-bin": "^4.3.7", "egg-bin": "^4.3.7",
"egg-ci": "^1.8.0", "egg-ci": "^1.8.0",
"egg-mock": "^3.15.0", "egg-mock": "^3.15.0",
"egg-scripts": "^2.5.1",
"egg-validate": "^2.0.2",
"egg-view-assets": "^1.6.1",
"egg-view-nunjucks": "^2.2.0",
"enzyme": "^3.9.0", "enzyme": "^3.9.0",
"eslint": "^4.18.2", "eslint": "^4.18.2",
"eslint-config-airbnb": "^17.0.0", "eslint-config-airbnb": "^17.0.0",
@ -68,15 +73,11 @@
"eslint-plugin-markdown": "^1.0.0-beta.6", "eslint-plugin-markdown": "^1.0.0-beta.6",
"eslint-plugin-react": "^7.11.1", "eslint-plugin-react": "^7.11.1",
"mockjs": "^1.0.1-beta3", "mockjs": "^1.0.1-beta3",
"node-fetch": "^2.6.1",
"redbox-react": "^1.5.0", "redbox-react": "^1.5.0",
"umi": "^2.1.2", "umi": "^2.1.2",
"umi-plugin-ga": "^1.1.3", "umi-plugin-ga": "^1.1.3",
"umi-plugin-react": "^1.1.1", "umi-plugin-react": "^1.1.1"
"egg": "^2.4.1",
"egg-scripts": "^2.5.1",
"egg-validate": "^2.0.2",
"egg-view-assets": "^1.6.1",
"egg-view-nunjucks": "^2.2.0"
}, },
"engines": { "engines": {
"node": ">=8.9.0" "node": ">=8.9.0"

View File

@ -161,7 +161,7 @@ let generateIndexLatencyData = (target)=>{
} }
let charts = [];
class SliderChart extends React.Component { class SliderChart extends React.Component {
constructor() { constructor() {
super(); super();
@ -198,11 +198,29 @@ let generateIndexLatencyData = (target)=>{
let pos = `${xname}*${yname}`; let pos = `${xname}*${yname}`;
return ( return (
<div style={{background:"#fff",padding: 10}}>
<Chart <Chart
data={this.props.data} data={this.props.data}
onGetG2Instance={c=>{
charts.push(c);
}}
onPlotMove={ev=>{
charts.forEach((chart)=>{
chart.showTooltip({
x: ev.x,
y: ev.y
});
});
}}
onPlotLeave={ev=>{
charts.forEach(chart=>{
chart.hideTooltip();
});
}}
scale={scale} scale={scale}
height={300} height={180}
forceFit forceFit
padding="auto"
// onGetG2Instance={g2Chart => { // onGetG2Instance={g2Chart => {
// chart = g2Chart; // chart = g2Chart;
// }} // }}
@ -211,7 +229,7 @@ let generateIndexLatencyData = (target)=>{
{this.props.title} {this.props.title}
</h3> </h3>
<Tooltip /> <Tooltip />
<Axis grid={grid} name={xname} title={axisTitle}/> <Axis grid={grid} name={xname} />
<Axis grid={grid} label={axisLabel} name={yname} title={axisTitle}/> <Axis grid={grid} label={axisLabel} name={yname} title={axisTitle}/>
<Legend /> <Legend />
<Geom <Geom
@ -230,18 +248,19 @@ let generateIndexLatencyData = (target)=>{
color="type" color="type"
/> />
</Chart> </Chart>
</div>
); );
} }
} }
const styles ={ const styles ={
mainTitle:{ mainTitle:{
fontSize:20, fontSize:14,
color:"black", color:"black",
textAlign:"center" textAlign:"center"
}, },
subTitle:{ subTitle:{
fontSize:16, fontSize:12,
color:"gray", color:"gray",
textAlign:"center" textAlign:"center"
} }
@ -284,74 +303,71 @@ class StatsCharts extends PureComponent {
indexLatency:[], indexLatency:[],
timeScale: {min: moment().subtract(1, 'h').valueOf(), max: new Date()}, timeScale: {min: moment().subtract(1, 'h').valueOf(), max: new Date()},
} }
componentDidMount(){ fetchData() {
let {dispatch} = this.props; let {dispatch} = this.props;
let me = this; dispatch({
setInterval(function(){ type: 'clusterMonitor/fetchClusterNodeStats',
dispatch({ callback: ({nodes_stats})=> {
type: 'clusterMonitor/fetchClusterNodeStats', let nodesStats = nodes_stats;
callback: ({nodes_stats})=> { console.log(nodesStats);
// var heapStats = [], cpuStats=[],searchLatency=[], indexLatency=[]; let nodeCpu = [],nodeHeap=[],nodeSearchLatency=[],nodeIndexLatency=[], readThreadQueue=[],writeThreadQueue=[];
// for(let st of nodes_stats){ //let now = moment(1607085646112);
// var fields = { for(let ns of nodesStats){
// time: st.timestamp, //let i = 0;
// type: st.node_name, for(let bk of ns.metrics.buckets){
// } let fields = {
// heapStats.push({ time: bk.key_as_string, //now.subtract(300-30*i , 's').valueOf(),
// ...fields, type: ns.key,
// heap_ratio: st.node_stats.jvm.mem.heap_used_percent, };
// }); nodeCpu.push({
// cpuStats.push({ ...fields,
// ...fields, cpu_ratio: bk.cpu_used.value,
// cpu_ratio: st.node_stats.process.cpu.percent, });
// }); nodeHeap.push({
// } ...fields,
let nodesStats = nodes_stats; heap_ratio: bk.heap_percent.value,
let nodeCpu = [],nodeHeap=[],nodeSearchLatency=[],nodeIndexLatency=[]; });
let now = moment(1607085646112); nodeSearchLatency.push({
for(let ns of nodesStats){ ...fields,
let i = 0; latency: bk.ds1 ? (bk.ds1.value/bk.ds.value).toFixed(2): 0,
for(let bk of ns.metrics.buckets){ });
let fields = { nodeIndexLatency.push({
time: now.subtract(300-30*i , 's').valueOf(), //bk.key_as_string, ...fields,
type: ns.key, latency: bk.ds1 ? (bk.ds4.value/bk.ds3.value).toFixed(2): 0,
}; });
i++; readThreadQueue.push({
nodeCpu.push({ ...fields,
...fields, queue: bk.read_threads_queue.value,
cpu_ratio: bk.cpu_used.value, });
}); writeThreadQueue.push({
nodeHeap.push({ ...fields,
...fields, queue: bk.write_threads_queue.value,
heap_ratio: bk.heap_percent.value, });
}); }
nodeSearchLatency.push({ }
...fields, this.setState({
latency: bk.ds1 ? (bk.ds1.value/bk.ds.value).toFixed(2): 0, heapStats: nodeHeap,
}); cpuStats: nodeCpu,
nodeIndexLatency.push({ searchLatency: nodeSearchLatency,
...fields, indexLatency: nodeIndexLatency,
latency: bk.ds1 ? (bk.ds4.value/bk.ds3.value).toFixed(2): 0, readThreadQueue: readThreadQueue,
}); writeThreadQueue: writeThreadQueue,
} timeScale: {min: moment().subtract(1, 'h').valueOf(), max: new Date()},
} });
me.setState({ }
heapStats: nodeHeap, });
cpuStats: nodeCpu, }
searchLatency: nodeSearchLatency, componentDidMount(){
indexLatency: nodeIndexLatency, this.fetchData();
timeScale: {min: moment().subtract(1, 'h').valueOf(), max: new Date()}, setInterval(() =>{
}); this.fetchData();
} }, 30000);
});
}, 10000);
} }
render(){ render(){
return ( return (
<div> <div>
<Row gutter={24} style={{marginBottom:10}}> <Row gutter={24} style={{marginBottom:10}}>
<Col xl={12} lg={24} md={24} sm={24} xs={24}> <Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Card>
<SliderChart title="内存使用占比(%)" xname="time" yname="heap_ratio" <SliderChart title="内存使用占比(%)" xname="time" yname="heap_ratio"
data={this.state.heapStats} data={this.state.heapStats}
unit="%" unit="%"
@ -374,11 +390,9 @@ class StatsCharts extends PureComponent {
} }
} }
} }
/> />
</Card>
</Col> </Col>
<Col xl={12} lg={24} md={24} sm={24} xs={24}> <Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Card>
<SliderChart title="CPU使用占比(%)" xname="time" yname="cpu_ratio" <SliderChart title="CPU使用占比(%)" xname="time" yname="cpu_ratio"
type="cpu_ratio" type="cpu_ratio"
data={this.state.cpuStats} data={this.state.cpuStats}
@ -404,13 +418,11 @@ class StatsCharts extends PureComponent {
} }
} }
/> />
</Card>
</Col> </Col>
</Row> </Row>
<Row gutter={24}> <Row gutter={24} style={{marginBottom:10}}>
<Col xl={12} lg={24} md={24} sm={24} xs={24}> <Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Card>
<SliderChart title="搜索延迟(ms)" xname="time" yname="latency" <SliderChart title="搜索延迟(ms)" xname="time" yname="latency"
type="search_latency" type="search_latency"
data={this.state.searchLatency} data={this.state.searchLatency}
@ -435,11 +447,9 @@ class StatsCharts extends PureComponent {
} }
} }
} }
/> />
</Card>
</Col> </Col>
<Col xl={12} lg={24} md={24} sm={24} xs={24}> <Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Card>
<SliderChart title="索引延迟(ms)" xname="time" yname="latency" <SliderChart title="索引延迟(ms)" xname="time" yname="latency"
type="index_latency" type="index_latency"
data={this.state.indexLatency} data={this.state.indexLatency}
@ -447,7 +457,7 @@ class StatsCharts extends PureComponent {
unit="ms" unit="ms"
scale={{ scale={{
time: { time: {
alias: "时间", // alias: "时间",
type: "time", type: "time",
mask: "HH:mm", mask: "HH:mm",
tickCount: 6, tickCount: 6,
@ -465,9 +475,58 @@ class StatsCharts extends PureComponent {
} }
} }
/> />
</Card>
</Col> </Col>
</Row></div> </Row>
<Row gutter={24} style={{marginBottom:10}}>
<Col xl={12} lg={24} md={24} sm={24} xs={24}>
<SliderChart title="索引线程" xname="time" yname="queue"
data={this.state.readThreadQueue}
unit=""
scale={{
time: {
alias: "时间",
type: "time",
mask: "HH:mm",
tickCount: 6,
...this.state.timeScale,
nice: false,
},
queue: {
min: 0,
},
type: {
type: "cat"
}
}
}
/>
</Col>
<Col xl={12} lg={24} md={24} sm={24} xs={24}>
<SliderChart title="读线程" xname="time" yname="queue"
data={this.state.writeThreadQueue}
unit=""
scale={{
time: {
// alias: "时间",
type: "time",
mask: "HH:mm",
tickCount: 6,
...this.state.timeScale,
nice: false,
},
queue: {
min: 0,
// max: 1
},
type: {
type: "cat"
}
}
}
/>
</Col>
</Row>
</div>
) )
} }
} }
@ -478,12 +537,18 @@ class StatsCharts extends PureComponent {
class ClusterMonitor extends PureComponent { class ClusterMonitor extends PureComponent {
state={ state={
} }
componentDidMount() { fetchData(){
const { dispatch } = this.props; const { dispatch } = this.props;
dispatch({ dispatch({
type: 'clusterMonitor/fetchClusterOverview', type: 'clusterMonitor/fetchClusterOverview',
}); });
} }
componentDidMount() {
this.fetchData();
setInterval(()=>{
this.fetchData();
}, 30000);
}
render() { render() {
let vstyle = { let vstyle = {
@ -517,7 +582,7 @@ class ClusterMonitor extends PureComponent {
<Statistic valueStyle={vstyle} title="在线时长" value={clusterStats.online_duration} /> <Statistic valueStyle={vstyle} title="在线时长" value={clusterStats.online_duration} />
</Col> </Col>
<Col md={2} xs={4}> <Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="健康情况" value={clusterStats.status} prefix={<HealthCircle color="yellow"/>} /> <Statistic valueStyle={vstyle} title="健康情况" value={clusterStats.status} prefix={<HealthCircle color={clusterStats.status}/>} />
</Col> </Col>
<Col md={2} xs={4}> <Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="节点数" value={clusterStats.nodes_count} /> <Statistic valueStyle={vstyle} title="节点数" value={clusterStats.nodes_count} />