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(`{
"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 {
'GET /dashboard/cluster/overview': function(req, res){
let cluster_stats = data.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,
}
}
}
}
};
res.send(result);
//console.log(typeof fetch);
getClusterOverview().then((result)=>{
//console.log(result);
res.send(result);
}).catch(err=>{
console.log(err);
});
},
'GET /dashboard/cluster/nodes_stats': function(req, res) {
// var statsData = [{
// node_name: nodesStats.source_node.name,
// 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,
// }
// }];
Promise.all([ getNodesStats()]).then((values) => {
//console.log(values);
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"
},
"devDependencies": {
"antd-pro-merge-less": "^0.1.0",
"antd-theme-webpack-plugin": "^1.1.8",
"autod": "^3.0.1",
"autod-egg": "^1.1.0",
"antd-pro-merge-less": "^0.1.0",
"babel-eslint": "^8.1.2",
"babel-plugin-dva-hmr": "^0.4.1",
"babel-plugin-import": "^1.6.3",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"egg": "^2.4.1",
"egg-bin": "^4.3.7",
"egg-ci": "^1.8.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",
"eslint": "^4.18.2",
"eslint-config-airbnb": "^17.0.0",
@ -68,15 +73,11 @@
"eslint-plugin-markdown": "^1.0.0-beta.6",
"eslint-plugin-react": "^7.11.1",
"mockjs": "^1.0.1-beta3",
"node-fetch": "^2.6.1",
"redbox-react": "^1.5.0",
"umi": "^2.1.2",
"umi-plugin-ga": "^1.1.3",
"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"
"umi-plugin-react": "^1.1.1"
},
"engines": {
"node": ">=8.9.0"

View File

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