From 0bbbc66182b6cfc357f2f381e062de326e1d5434 Mon Sep 17 00:00:00 2001 From: silenceqi Date: Mon, 14 Dec 2020 20:59:35 +0800 Subject: [PATCH] add time range query --- .../calculate_timeseries_interval.js | 85 +++ web/mock/dashboard/cluster_monitor.js | 446 ++++++++----- web/package.json | 2 + web/src/pages/Dashboard/ClusterMonitor.js | 592 ++++++++++-------- web/src/pages/Dashboard/models/cluster.js | 4 +- web/src/services/dashboard.js | 8 +- web/src/utils/request.js | 2 +- 7 files changed, 701 insertions(+), 438 deletions(-) create mode 100644 web/mock/dashboard/calculate_timeseries_interval.js diff --git a/web/mock/dashboard/calculate_timeseries_interval.js b/web/mock/dashboard/calculate_timeseries_interval.js new file mode 100644 index 00000000..ac8ba2d7 --- /dev/null +++ b/web/mock/dashboard/calculate_timeseries_interval.js @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import moment from 'moment'; + +const d = moment.duration; +const roundingRules = [ + [d(500, 'ms'), d(100, 'ms')], + [d(5, 'second'), d(1, 'second')], + [d(7.5, 'second'), d(5, 'second')], + [d(15, 'second'), d(10, 'second')], + [d(45, 'second'), d(30, 'second')], + [d(3, 'minute'), d(1, 'minute')], + [d(9, 'minute'), d(5, 'minute')], + [d(20, 'minute'), d(10, 'minute')], + [d(45, 'minute'), d(30, 'minute')], + [d(2, 'hour'), d(1, 'hour')], + [d(6, 'hour'), d(3, 'hour')], + [d(24, 'hour'), d(12, 'hour')], + [d(1, 'week'), d(1, 'd')], + [d(3, 'week'), d(1, 'week')], + [d(1, 'year'), d(1, 'month')], + [Infinity, d(1, 'year')], +]; + +function find(rules, check) { + function pick(buckets, duration) { + const target = duration / buckets; + let lastResp; + + for (let i = 0; i < rules.length; i++) { + const rule = rules[i]; + const resp = check(rule[0], rule[1], target); + + if (resp == null) { + if (lastResp) { + return lastResp; + } + break; + } + + lastResp = resp; + } + + // fallback to just a number of milliseconds, ensure ms is >= 1 + const ms = Math.max(Math.floor(target), 1); + return moment.duration(ms, 'ms'); + } + + return function (buckets, duration) { + const interval = pick(buckets, duration); + if (interval) { + return moment.duration(interval._data); + } + }; +} + +const revRoundingRules = roundingRules.slice(0).reverse(); + +/* + * 24 hours: 600 seconds + * 12 hours: 300 seconds + * 4 hours: 60 seconds + * 1 hour: 30 seconds + * 15 minutes: 10 seconds + */ +export const calculateAuto = find(revRoundingRules, (bound, interval, target) => { + if (bound > target) { + return interval; + } +}); + + +export function calculateTimeseriesInterval( + lowerBoundInMsSinceEpoch, + upperBoundInMsSinceEpoch, + minIntervalSeconds +) { + const duration = moment.duration(upperBoundInMsSinceEpoch - lowerBoundInMsSinceEpoch, 'ms'); + + return Math.max(minIntervalSeconds, calculateAuto(100, duration).asSeconds()); +} \ No newline at end of file diff --git a/web/mock/dashboard/cluster_monitor.js b/web/mock/dashboard/cluster_monitor.js index ad8b7c4a..c9ee3e0a 100644 --- a/web/mock/dashboard/cluster_monitor.js +++ b/web/mock/dashboard/cluster_monitor.js @@ -1,4 +1,10 @@ import fetch from 'node-fetch'; +import moment from 'moment'; +import {calculateTimeseriesInterval} from './calculate_timeseries_interval'; +import { promises } from 'dns'; +//import {formatTimestampToDuration} from './format_timestamp_to_duration'; +const minIntervalSeconds = 10; + let data = JSON.parse(`{ "cluster_stats" : { @@ -319,6 +325,20 @@ let data = JSON.parse(`{ }, "search_query_time" : { "value" : 7314.0 + }, + "read_threads_queue" : { + "value" : 0.0 + }, + "write_threads_queue" : { + "value" : 0.0 + }, + "search_qps" : { + "value" : 0.0, + "normalized_value" : 2.1 + }, + "index_qps" : { + "value" : 165.0, + "normalized_value" : 5.9 } }, { @@ -357,6 +377,20 @@ let data = JSON.parse(`{ }, "ds4" : { "value" : 102.0 + }, + "read_threads_queue" : { + "value" : 0.0 + }, + "write_threads_queue" : { + "value" : 0.0 + }, + "search_qps" : { + "value" : 0.0, + "normalized_value" : 1.8 + }, + "index_qps" : { + "value" : 165.0, + "normalized_value" : 5.3 } }, { @@ -395,6 +429,20 @@ let data = JSON.parse(`{ }, "ds4" : { "value" : 144.0 + }, + "read_threads_queue" : { + "value" : 0.0 + }, + "write_threads_queue" : { + "value" : 0.0 + }, + "search_qps" : { + "value" : 0.0, + "normalized_value" : 2.5 + }, + "index_qps" : { + "value" : 165.0, + "normalized_value" : 5.0 } }, { @@ -433,6 +481,20 @@ let data = JSON.parse(`{ }, "ds4" : { "value" : 123.0 + }, + "read_threads_queue" : { + "value" : 0.0 + }, + "write_threads_queue" : { + "value" : 0.0 + }, + "search_qps" : { + "value" : 0.0, + "normalized_value" : 3.0 + }, + "index_qps" : { + "value" : 165.0, + "normalized_value" : 5.8 } }, { @@ -471,6 +533,20 @@ let data = JSON.parse(`{ }, "ds4" : { "value" : 19.0 + }, + "read_threads_queue" : { + "value" : 0.0 + }, + "write_threads_queue" : { + "value" : 0.0 + }, + "search_qps" : { + "value" : 0.0, + "normalized_value" : 0.0 + }, + "index_qps" : { + "value" : 165.0, + "normalized_value" : 5.5 } } ] @@ -478,180 +554,193 @@ let data = JSON.parse(`{ } ]`); + function getOverviewBody(params){ + let body = { + _source: [ "cluster_stats"], + size: 1, + sort: [ + { + timestamp: { + order: "desc" + } + } + ], + query: { + bool: { + must: [ + { + match: { + type: "cluster_stats" + } + } + ], + filter: [ + { + range: { + timestamp: { + "gte": params.timeRange.min, + lte: params.timeRange.max + } + } + } + ] + } + } + }; + return JSON.stringify(body); + } + +function getNodesStatsBody(params){ + let min = moment(params.timeRange.min).valueOf(); + let max = moment(params.timeRange.max).valueOf(); + const bucketSizeInSeconds = calculateTimeseriesInterval(min, max, minIntervalSeconds); + console.log(bucketSizeInSeconds); + let body = { + "size": 0, + "query": { + "bool": { + "must": [ + { + "match": { + "type": "node_stats" + } + } + ], + "filter": [ + { + "range": { + "timestamp": { + "gte": params.timeRange.min, + "lte": params.timeRange.max + } + } + } + ] + } + }, + "aggs": { + "nodes": { + "terms": { + "field": "source_node.name", + "size": 10 + }, + "aggs": { + "metrics": { + "date_histogram": { + "field": "timestamp", + "fixed_interval": bucketSizeInSeconds + 's' + }, + "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" + } + }, + "search_qps":{ + "derivative": { + "buckets_path": "search_query_total", + "gap_policy": "skip", + "unit": "1s" + } + }, + "index_qps":{ + "derivative": { + "buckets_path": "index_total", + "gap_policy": "skip", + "unit": "1s" + } + }, + "read_threads_queue":{ + "max": { + "field": "node_stats.thread_pool.get.queue" + } + }, + "write_threads_queue":{ + "max": { + "field": "node_stats.thread_pool.write.queue" + } + } + } + } + } + } + } + }; + return JSON.stringify(body); +} + 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" - } - }, - "search_qps":{ - "derivative": { - "buckets_path": "search_query_total", - "gap_policy": "skip", - "unit": "1s" - } - }, - "index_qps":{ - "derivative": { - "buckets_path": "index_total", - "gap_policy": "skip", - "unit": "1s" - } - }, - "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'; + const gatewayUrl = 'http://localhost:9200'; -function getClusterOverview(){ +function getClusterOverview(params){ return fetch(gatewayUrl+apiUrls.CLUSTER_OVERVIEW.path, { method: 'POST', - body: apiUrls.CLUSTER_OVERVIEW.body, + body: getOverviewBody(params), headers:{ 'Content-Type': 'application/json' } }).then(esRes=>{ return esRes.json(); }).then(rel=>{ + //console.log(rel); if(rel.hits.hits.length>0){ var rdata = rel.hits.hits[0]._source; }else{ @@ -681,19 +770,20 @@ function getClusterOverview(){ } } }; - return result; + return Promise.resolve(result); }); } -function getNodesStats(){ +function getNodesStats(params){ return fetch(gatewayUrl+apiUrls.GET_ES_NODE_STATS.path, { method: 'POST', - body: apiUrls.GET_ES_NODE_STATS.body, + body: getNodesStatsBody(params), headers:{ 'Content-Type': 'application/json' } }).then(esRes=>{ - return esRes.json(); + return esRes.json(); + // return esRes.json(); }).then(rel=>{ //console.log(rel); if(rel.aggregations.nodes.buckets.length>0){ @@ -702,21 +792,41 @@ function getNodesStats(){ }else{ rdata = nodesStats; } - return rdata; + return Promise.resolve(rdata); }); } export default { - 'GET /dashboard/cluster/overview': function(req, res){ - //console.log(typeof fetch); - getClusterOverview().then((result)=>{ - //console.log(result); - res.send(result); - }).catch(err=>{ - console.log(err); + 'POST /dashboard/cluster/overview': function(req, res){ + // console.log(1, req.body); + let params = req.body; + !params.timeRange && (params.timeRange={ + min: 'now-1h', + max: 'now' }); + Promise.all([getClusterOverview(params),getNodesStats(params)]).then(function(values){ + let robj = values[0]; + robj = Object.assign(robj, {nodes_stats: values[1]}); + res.send(robj); + }).catch(function(err){ + console.log(err); + }); + // getClusterOverview(params).then((result)=>{ + // //console.log(result); + // res.send(result); + // }).catch(err=>{ + // console.log(err); + // }); }, 'GET /dashboard/cluster/nodes_stats': function(req, res) { + let min = moment(1607839878669 - 2592000000).valueOf(); + const max = moment(1607839878669).valueOf(); + const bucketSizeInSeconds = calculateTimeseriesInterval(min, max, minIntervalSeconds); + const now = moment(); + const timestamp = moment(now).add(bucketSizeInSeconds, 'seconds'); // clone the `now` object + + //console.log(bucketSizeInSeconds); //, formatTimestampToDuration(timestamp, 'until', now)); + Promise.all([ getNodesStats()]).then((values) => { //console.log(values); res.send({ diff --git a/web/package.json b/web/package.json index 7fa30ccc..a89313eb 100644 --- a/web/package.json +++ b/web/package.json @@ -6,6 +6,7 @@ "@ant-design/charts": "^1.0.4", "@ant-design/icons": "^4.0.0", "@antv/data-set": "^0.9.6", + "@antv/g2-brush": "^0.0.2", "@babel/runtime": "^7.1.2", "antd": "^3.26.18", "antd-table-infinity": "^1.1.6", @@ -73,6 +74,7 @@ "eslint-plugin-markdown": "^1.0.0-beta.6", "eslint-plugin-react": "^7.11.1", "mockjs": "^1.0.1-beta3", + "moment-duration-format": "^2.3.2", "node-fetch": "^2.6.1", "redbox-react": "^1.5.0", "umi": "^2.1.2", diff --git a/web/src/pages/Dashboard/ClusterMonitor.js b/web/src/pages/Dashboard/ClusterMonitor.js index 8bed5887..b48a9ab5 100644 --- a/web/src/pages/Dashboard/ClusterMonitor.js +++ b/web/src/pages/Dashboard/ClusterMonitor.js @@ -1,8 +1,10 @@ import React, { PureComponent,Fragment } from 'react'; import { connect } from 'dva'; import { formatMessage, FormattedMessage } from 'umi/locale'; -import { Row, Col, Card,Statistic,Icon, Divider, Skeleton } from 'antd'; -import moment from 'moment'; +import { Row, Col, Card,Statistic,Icon, Divider, DatePicker, Input, Dropdown, Menu, Button, InputNumber, Select } from 'antd'; +import moment, { relativeTimeRounding } from 'moment'; +import Brush from '@antv/g2-brush'; +const { RangePicker } = DatePicker; import { @@ -13,153 +15,6 @@ import { Tooltip, Legend, } from 'bizcharts'; -import { func } from 'prop-types'; - -let generateHeapData = (target)=>{ - let data = []; - let generator = (initTime) => { - var now = new Date(); - var time = initTime||now.getTime(); - var heap1 = ~~(Math.random() * 500) + 200; - var heap2 = ~~(Math.random() * 300) + 512; - if (data.length >= 120) { - data.shift(); - data.shift(); - } - - data.push({ - time: time, - heap_ratio: (heap1 *100) /1024, - type: "node1" - }); - data.push({ - time: time, - heap_ratio: (heap2 *100)/1024, - type: "node2" - }); - !initTime && target.setState({ - data - }); - }; - let stime = new Date(); - for(let i=120;i>0;i--){ - generator(new Date(stime.valueOf()- i * 1000 * 30)); - } - target.setState({ - data - }); - setInterval(()=>{generator(null)}, 30000); -} - -let generateCpuData = (target)=>{ - let data = []; - let generator = (initTime) => { - var now = new Date(); - var time = initTime || now.getTime(); - var cpu1 = ~~(Math.random()*5) + 0.1; - var cpu2 = ~~(Math.random()*3) +0.2; - if (data.length >= 120) { - data.shift(); - data.shift(); - } - - data.push({ - time: time, - cpu_ratio: cpu1, - type: "node1" - }); - data.push({ - time: time, - cpu_ratio: cpu2, - type: "node2" - }); - !initTime && target.setState({ - data - }); - }; - let stime = new Date(); - for(let i=120;i>0;i--){ - generator(new Date(stime.valueOf()- i * 1000 * 30)); - } - target.setState({ - data - }); - setInterval(()=>{generator(null)}, 30000); -} - -let generateSearchLatencyData = (target)=>{ - let data = []; - let generator = (initTime) => { - var now = new Date(); - var time = initTime || now.getTime(); - var latency1 = ~~(Math.random()*100) + 10; - var latency2 = ~~(Math.random()*150) +30; - if (data.length >= 120) { - data.shift(); - data.shift(); - } - - data.push({ - time: time, - latency: latency1, - type: "node1" - }); - data.push({ - time: time, - latency: latency2, - type: "node2" - }); - !initTime && target.setState({ - data - }); - }; - let stime = new Date(); - for(let i=120;i>0;i--){ - generator(new Date(stime.valueOf()- i * 1000 * 30)); - } - target.setState({ - data - }); - setInterval(()=>{generator(null)}, 30000); -} - -let generateIndexLatencyData = (target)=>{ - let data = []; - let generator = (initTime) => { - var now = new Date(); - var time = initTime || now.getTime(); - var latency1 = ~~(Math.random()*400) + 50; - var latency2 = ~~(Math.random()*500) +20; - if (data.length >= 120) { - data.shift(); - data.shift(); - } - - data.push({ - time: time, - latency: latency1, - type: "node1" - }); - data.push({ - time: time, - latency: latency2, - type: "node2" - }); - !initTime && target.setState({ - data - }); - }; - - let stime = new Date(); - for(let i=120;i>0;i--){ - generator(new Date(stime.valueOf()- i * 1000 * 30)); - } - target.setState({ - data - }); - setInterval(()=>{generator(null)}, 30000); -} - let charts = []; class SliderChart extends React.Component { @@ -201,8 +56,25 @@ let generateIndexLatencyData = (target)=>{
{ - charts.push(c); + onGetG2Instance={chart=>{ + //c.interaction('brush'); + let brushend = this.props.onBrushend; + new Brush({ + canvas: chart.get('canvas'), + chart, + type: 'X', + onBrushstart() { + chart.hideTooltip(); + }, + onBrushmove() { + chart.hideTooltip(); + }, + onBrushend(ev){ + this.container.clear(); + brushend(ev); + } + }); + charts.push(chart); }} onPlotMove={ev=>{ charts.forEach((chart)=>{ @@ -221,9 +93,6 @@ let generateIndexLatencyData = (target)=>{ height={180} forceFit padding="auto" - // onGetG2Instance={g2Chart => { - // chart = g2Chart; - // }} >

{this.props.title} @@ -235,7 +104,7 @@ let generateIndexLatencyData = (target)=>{ @@ -301,70 +170,67 @@ class StatsCharts extends PureComponent { cpuStats:[], searchLatency:[], indexLatency:[], - timeScale: {min: moment().subtract(1, 'h').valueOf(), max: new Date()}, + // timeScale: {min: moment().subtract(1, 'h').valueOf(), max: new Date()}, } - fetchData() { - let {dispatch} = this.props; - dispatch({ - type: 'clusterMonitor/fetchClusterNodeStats', - callback: ({nodes_stats})=> { - let nodesStats = nodes_stats; - //console.log(nodesStats); - let nodeCpu = [],nodeHeap=[],nodeSearchLatency=[],nodeIndexLatency=[], readThreadQueue=[],writeThreadQueue=[], - searchQps=[], indexQps=[]; - //let now = moment(1607085646112); - let startTime = new Date(); - let startMinutes = (Math.floor(startTime.getMinutes()/10)*10 + 70) % 60; - startTime.setMinutes(startMinutes == 0 ? 60: startMinutes, 0,0); - let vtime = startTime.valueOf(); - let ticks = []; - for(let i=0; i<6;i++){ - ticks.push( - new Date(vtime + (i - 6) * 10 * 60 *1000 ).toUTCString() - ); + formatData(nodes_stats) { + let nodesStats = nodes_stats || []; + let nodeCpu = [],nodeHeap=[],nodeSearchLatency=[],nodeIndexLatency=[], readThreadQueue=[],writeThreadQueue=[], + searchQps=[], indexQps=[]; + // let startTime = new Date(); + // let startMinutes = (Math.floor(startTime.getMinutes()/10)*10 + 70) % 60; + // startTime.setMinutes(startMinutes == 0 ? 60: startMinutes, 0,0); + // let vtime = startTime.valueOf(); + // let ticks = []; + // for(let i=0; i<6;i++){ + // ticks.push( + // new Date(vtime + (i - 6) * 10 * 60 *1000 ).toUTCString() + // ); + // } + 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, + }); + bk.ds1 && nodeSearchLatency.push({ + ...fields, + latency: bk.ds1.value ? (bk.ds1.value/bk.ds.value).toFixed(2) : null, + }); + bk.ds4 && nodeIndexLatency.push({ + ...fields, + latency: bk.ds4.value? (bk.ds4.value/bk.ds3.value).toFixed(2): null, + }); + readThreadQueue.push({ + ...fields, + queue: bk.read_threads_queue.value, + }); + writeThreadQueue.push({ + ...fields, + queue: bk.write_threads_queue.value, + }); + // if(bk.search_qps && bk.search_qps.normalized_value && bk.search_qps.normalized_value < 0){ + // console.log(bk.key_as_string, bk.search_qps.normalized_value, bk.search_qps.value); + // } + bk.search_qps && searchQps.push({ + ...fields, + qps: bk.search_qps.normalized_value ? bk.search_qps.normalized_value.toFixed(2) : null, + }); + bk.index_qps && indexQps.push({ + ...fields, + qps: bk.index_qps.normalized_value ? bk.index_qps.normalized_value.toFixed(2): null, + }); + } } - 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, - }); - bk.ds1 && nodeSearchLatency.push({ - ...fields, - latency: bk.ds1.value ? (bk.ds1.value/bk.ds.value).toFixed(2) : null, - }); - bk.ds4 && nodeIndexLatency.push({ - ...fields, - latency: bk.ds4.value? (bk.ds4.value/bk.ds3.value).toFixed(2): null, - }); - readThreadQueue.push({ - ...fields, - queue: bk.read_threads_queue.value, - }); - writeThreadQueue.push({ - ...fields, - queue: bk.write_threads_queue.value, - }); - bk.search_qps && searchQps.push({ - ...fields, - qps: bk.search_qps.normalized_value ? bk.search_qps.normalized_value.toFixed(2) : null, - }); - bk.index_qps && indexQps.push({ - ...fields, - qps: bk.index_qps.normalized_value ? bk.index_qps.normalized_value.toFixed(2): null, - }); - } - } - this.setState({ + return { heapStats: nodeHeap, cpuStats: nodeCpu, searchLatency: nodeSearchLatency, @@ -373,34 +239,34 @@ class StatsCharts extends PureComponent { writeThreadQueue: writeThreadQueue, searchQps: searchQps, indexQps: indexQps, - //timeScale: {min: moment().subtract(1, 'h').valueOf(), max: new Date()}, - timeScale:{ticks: ticks, min: moment().subtract(1, 'h').valueOf(), max: moment().add(1, 'm').valueOf()} - }); - } - }); + }; + + // timeScale:{ticks: ticks, min: moment().subtract(1, 'h').valueOf(), max: moment().add(1, 'm').valueOf()} } componentDidMount(){ - this.fetchData(); - setInterval(() =>{ - this.fetchData(); - }, 30000); + // this.fetchData(); + // tv = setInterval(() =>{ + // this.fetchData(); + // }, 10000); } render(){ + let data = this.formatData(this.props.data); return (
({ clusterMonitor })) class ClusterMonitor extends PureComponent { - state={ + constructor(props) { + super(props); + //this.timePicker = React.createRef(); + this.handleChartBrush = this.handleChartBrush.bind(this); } - fetchData(){ + state={ + timeRange: { + min: moment().subtract(1, 'h').toISOString(), + max: moment().toISOString() + }, + lastSeconds: 3600, + qsVisible: false, + } + fetchData = () => { + fetchDataCount++; + console.log(fetchDataCount, moment().diff(startTime)/1000); const { dispatch } = this.props; + let {timeRange, lastSeconds } = this.state; + if(lastSeconds && lastSeconds > 0){ + timeRange = { + min: moment().subtract(lastSeconds, 's').toISOString(), + max: moment().toISOString(), + }; + this.setState({ + pickerValue: [moment().subtract(lastSeconds, 's'), moment()], + }); + //this.timePicker.current.value= [moment().subtract(lastSeconds, 's'), moment()]; + } + let msDiff = moment(timeRange.max).diff(moment(timeRange.min)); + let timeMask = 'HH:mm'; + //console.log(msDiff); + if(msDiff > 1000 * 3600 + 5 * 1000 && msDiff <= 1000 * 3600 * 24 * 5 ){ + timeMask = 'MM-DD HH' + }else if (msDiff > 1000 * 3600 * 24 * 5){ + timeMask = 'MM-DD' + } + this.setState({timeScale: {min: timeRange.min, max: timeRange.max, mask: timeMask}}); dispatch({ type: 'clusterMonitor/fetchClusterOverview', + payload: { + timeRange: timeRange, + }, }); } + componentWillUnmount(){ + clearInterval(tv1); + } componentDidMount() { this.fetchData(); - setInterval(()=>{ + // tv1 = setInterval(()=>{ + // this.fetchData(); + // }, 10000); + this.autoRefresh(); + } + + autoRefresh(durationInSeconds){ + !durationInSeconds && (durationInSeconds = 10); + clearInterval(tv1); + tv1 = setInterval(()=>{ this.fetchData(); - }, 30000); + }, durationInSeconds * 1000); + } + + onTimeOk = (values) => { + //console.log('onOk: ', values); + const min = values[0].toISOString(); + const max = values[1].toISOString(); + this.setState({ + timeRange: { + min: min, + max: max + }, + lastSeconds: 0, + }, ()=>{ + this.fetchData(); + }); + } + onTimeChange = (values) => { + this.setState({ + pickerValue: values, + }); + } + + handleQuickSelect = (ev) =>{ + let lastSeconds = 0; + switch(ev.key){ + case "2": + lastSeconds = 3600 * 24; + break; + case "3": + lastSeconds = 3600 * 24 * 7; + break; + case "4": + lastSeconds = 3600 * 24 * 30; + break; + case "5": + return; + default: + lastSeconds = 60 * 60; + } + this.setState({ + lastSeconds: lastSeconds, + qsVisible: false, + },()=>{ + this.fetchData(); + }); + } + + handleQSVisibleChange = flag => { + this.setState({ qsVisible: flag }); + }; + + handleChartBrush(ev){ + let dtimes = ev.time; + if(dtimes.length < 2) + return; + let timeRange = { + min: dtimes[0], + max: dtimes[1], + } + this.setState({ + timeRange: timeRange, + lastSeconds: 0, + pickerValue:[moment(dtimes[0]), moment(dtimes[1])], + },()=>{ + this.fetchData(); + }); + } + + handleAutoRefresh = ()=>{ + let unit = this.refreshUnit.rcSelect.state.value[0]; + let base = 1; + switch(unit){ + case "minutes": + base *= 60; + break; + case "hours": + base *= 3600 + break; + } + let durationInSeconds = this.refreshNum.inputNumberRef.state.value * base; + this.autoRefresh(durationInSeconds); } render() { @@ -627,8 +631,64 @@ class ClusterMonitor extends PureComponent { online_duration: moment.duration(rawStats.nodes.jvm.max_uptime_in_millis).humanize(), }; } + //console.log(clusterMonitor.nodes_stats); + const menu = ( + + + 最近一小时 + + + 最近一天 + + + 最近一周 + + + 最近一个月 + + + + + + this.refreshNum = el} /> + + + + + + ); return (
+
+ + + + + + + +
@@ -663,7 +723,9 @@ class ClusterMonitor extends PureComponent { - +
); } diff --git a/web/src/pages/Dashboard/models/cluster.js b/web/src/pages/Dashboard/models/cluster.js index 660cd809..b4e0187c 100644 --- a/web/src/pages/Dashboard/models/cluster.js +++ b/web/src/pages/Dashboard/models/cluster.js @@ -6,8 +6,8 @@ export default { }, effects:{ - *fetchClusterOverview({callback}, {call, put}){ - let clusterData = yield call(getClusterOverview); + *fetchClusterOverview({payload, callback}, {call, put}){ + let clusterData = yield call(getClusterOverview, payload); yield put({type: 'saveData', payload: clusterData}) if(callback && typeof callback == 'function'){ callback(clusterData); diff --git a/web/src/services/dashboard.js b/web/src/services/dashboard.js index 966cbfd0..01ebbeb1 100644 --- a/web/src/services/dashboard.js +++ b/web/src/services/dashboard.js @@ -1,7 +1,11 @@ import request from '@/utils/request'; -export async function getClusterOverview(){ - return request('/dashboard/cluster/overview'); +export async function getClusterOverview(payload){ + return request('/dashboard/cluster/overview',{ + method: 'POST', + body: payload, + expirys: 0, + }); } export async function getClusterNodeStats(){ diff --git a/web/src/utils/request.js b/web/src/utils/request.js index 90703f75..a8383900 100644 --- a/web/src/utils/request.js +++ b/web/src/utils/request.js @@ -123,7 +123,7 @@ export default function request( } return fetch(url, newOptions) .then(checkStatus) - .then(response => cachedSave(response, hashcode)) + // .then(response => cachedSave(response, hashcode)) .then(response => { // DELETE and 204 do not return data by default // using .json will report an error.