add time range query

This commit is contained in:
silenceqi 2020-12-14 20:59:35 +08:00
parent c478823d4c
commit 0bbbc66182
7 changed files with 701 additions and 438 deletions

View File

@ -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());
}

View File

@ -1,4 +1,10 @@
import fetch from 'node-fetch'; 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(`{ let data = JSON.parse(`{
"cluster_stats" : { "cluster_stats" : {
@ -319,6 +325,20 @@ let data = JSON.parse(`{
}, },
"search_query_time" : { "search_query_time" : {
"value" : 7314.0 "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" : { "ds4" : {
"value" : 102.0 "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" : { "ds4" : {
"value" : 144.0 "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" : { "ds4" : {
"value" : 123.0 "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" : { "ds4" : {
"value" : 19.0 "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 = { const apiUrls = {
CLUSTER_OVERVIEW: { CLUSTER_OVERVIEW: {
path:'/.monitoring-es-*/_search', 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":{ "GET_ES_NODE_STATS":{
path: '/.monitoring-es-*/_search', 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, { return fetch(gatewayUrl+apiUrls.CLUSTER_OVERVIEW.path, {
method: 'POST', method: 'POST',
body: apiUrls.CLUSTER_OVERVIEW.body, body: getOverviewBody(params),
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
}).then(esRes=>{ }).then(esRes=>{
return esRes.json(); return esRes.json();
}).then(rel=>{ }).then(rel=>{
//console.log(rel);
if(rel.hits.hits.length>0){ if(rel.hits.hits.length>0){
var rdata = rel.hits.hits[0]._source; var rdata = rel.hits.hits[0]._source;
}else{ }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, { return fetch(gatewayUrl+apiUrls.GET_ES_NODE_STATS.path, {
method: 'POST', method: 'POST',
body: apiUrls.GET_ES_NODE_STATS.body, body: getNodesStatsBody(params),
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
}).then(esRes=>{ }).then(esRes=>{
return esRes.json(); return esRes.json();
// return esRes.json();
}).then(rel=>{ }).then(rel=>{
//console.log(rel); //console.log(rel);
if(rel.aggregations.nodes.buckets.length>0){ if(rel.aggregations.nodes.buckets.length>0){
@ -702,21 +792,41 @@ function getNodesStats(){
}else{ }else{
rdata = nodesStats; rdata = nodesStats;
} }
return rdata; return Promise.resolve(rdata);
}); });
} }
export default { export default {
'GET /dashboard/cluster/overview': function(req, res){ 'POST /dashboard/cluster/overview': function(req, res){
//console.log(typeof fetch); // console.log(1, req.body);
getClusterOverview().then((result)=>{ let params = req.body;
//console.log(result); !params.timeRange && (params.timeRange={
res.send(result); min: 'now-1h',
}).catch(err=>{ max: 'now'
console.log(err);
}); });
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) { '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) => { Promise.all([ getNodesStats()]).then((values) => {
//console.log(values); //console.log(values);
res.send({ res.send({

View File

@ -6,6 +6,7 @@
"@ant-design/charts": "^1.0.4", "@ant-design/charts": "^1.0.4",
"@ant-design/icons": "^4.0.0", "@ant-design/icons": "^4.0.0",
"@antv/data-set": "^0.9.6", "@antv/data-set": "^0.9.6",
"@antv/g2-brush": "^0.0.2",
"@babel/runtime": "^7.1.2", "@babel/runtime": "^7.1.2",
"antd": "^3.26.18", "antd": "^3.26.18",
"antd-table-infinity": "^1.1.6", "antd-table-infinity": "^1.1.6",
@ -73,6 +74,7 @@
"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",
"moment-duration-format": "^2.3.2",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"redbox-react": "^1.5.0", "redbox-react": "^1.5.0",
"umi": "^2.1.2", "umi": "^2.1.2",

View File

@ -1,8 +1,10 @@
import React, { PureComponent,Fragment } from 'react'; import React, { PureComponent,Fragment } from 'react';
import { connect } from 'dva'; import { connect } from 'dva';
import { formatMessage, FormattedMessage } from 'umi/locale'; import { formatMessage, FormattedMessage } from 'umi/locale';
import { Row, Col, Card,Statistic,Icon, Divider, Skeleton } from 'antd'; import { Row, Col, Card,Statistic,Icon, Divider, DatePicker, Input, Dropdown, Menu, Button, InputNumber, Select } from 'antd';
import moment from 'moment'; import moment, { relativeTimeRounding } from 'moment';
import Brush from '@antv/g2-brush';
const { RangePicker } = DatePicker;
import { import {
@ -13,153 +15,6 @@ import {
Tooltip, Tooltip,
Legend, Legend,
} from 'bizcharts'; } 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 = []; let charts = [];
class SliderChart extends React.Component { class SliderChart extends React.Component {
@ -201,8 +56,25 @@ let generateIndexLatencyData = (target)=>{
<div style={{background:"#fff",padding: 10}}> <div style={{background:"#fff",padding: 10}}>
<Chart <Chart
data={this.props.data} data={this.props.data}
onGetG2Instance={c=>{ onGetG2Instance={chart=>{
charts.push(c); //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=>{ onPlotMove={ev=>{
charts.forEach((chart)=>{ charts.forEach((chart)=>{
@ -221,9 +93,6 @@ let generateIndexLatencyData = (target)=>{
height={180} height={180}
forceFit forceFit
padding="auto" padding="auto"
// onGetG2Instance={g2Chart => {
// chart = g2Chart;
// }}
> >
<h3 className='main-title' style={styles.mainTitle}> <h3 className='main-title' style={styles.mainTitle}>
{this.props.title} {this.props.title}
@ -235,7 +104,7 @@ let generateIndexLatencyData = (target)=>{
<Geom <Geom
type="line" type="line"
position={pos} position={pos}
color={["type"]} //["#ff7f0e", "#2ca02c"] color={["type"]}
shape="line" shape="line"
size={2} size={2}
/> />
@ -301,70 +170,67 @@ class StatsCharts extends PureComponent {
cpuStats:[], cpuStats:[],
searchLatency:[], searchLatency:[],
indexLatency:[], indexLatency:[],
timeScale: {min: moment().subtract(1, 'h').valueOf(), max: new Date()}, // timeScale: {min: moment().subtract(1, 'h').valueOf(), max: new Date()},
} }
fetchData() { formatData(nodes_stats) {
let {dispatch} = this.props; let nodesStats = nodes_stats || [];
dispatch({ let nodeCpu = [],nodeHeap=[],nodeSearchLatency=[],nodeIndexLatency=[], readThreadQueue=[],writeThreadQueue=[],
type: 'clusterMonitor/fetchClusterNodeStats', searchQps=[], indexQps=[];
callback: ({nodes_stats})=> { // let startTime = new Date();
let nodesStats = nodes_stats; // let startMinutes = (Math.floor(startTime.getMinutes()/10)*10 + 70) % 60;
//console.log(nodesStats); // startTime.setMinutes(startMinutes == 0 ? 60: startMinutes, 0,0);
let nodeCpu = [],nodeHeap=[],nodeSearchLatency=[],nodeIndexLatency=[], readThreadQueue=[],writeThreadQueue=[], // let vtime = startTime.valueOf();
searchQps=[], indexQps=[]; // let ticks = [];
//let now = moment(1607085646112); // for(let i=0; i<6;i++){
let startTime = new Date(); // ticks.push(
let startMinutes = (Math.floor(startTime.getMinutes()/10)*10 + 70) % 60; // new Date(vtime + (i - 6) * 10 * 60 *1000 ).toUTCString()
startTime.setMinutes(startMinutes == 0 ? 60: startMinutes, 0,0); // );
let vtime = startTime.valueOf(); // }
let ticks = []; for(let ns of nodesStats){
for(let i=0; i<6;i++){ //let i = 0;
ticks.push( for(let bk of ns.metrics.buckets){
new Date(vtime + (i - 6) * 10 * 60 *1000 ).toUTCString() 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){ return {
//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({
heapStats: nodeHeap, heapStats: nodeHeap,
cpuStats: nodeCpu, cpuStats: nodeCpu,
searchLatency: nodeSearchLatency, searchLatency: nodeSearchLatency,
@ -373,34 +239,34 @@ class StatsCharts extends PureComponent {
writeThreadQueue: writeThreadQueue, writeThreadQueue: writeThreadQueue,
searchQps: searchQps, searchQps: searchQps,
indexQps: indexQps, 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(){ componentDidMount(){
this.fetchData(); // this.fetchData();
setInterval(() =>{ // tv = setInterval(() =>{
this.fetchData(); // this.fetchData();
}, 30000); // }, 10000);
} }
render(){ render(){
let data = this.formatData(this.props.data);
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}>
<SliderChart title="内存使用占比(%)" xname="time" yname="heap_ratio" <SliderChart title="内存使用占比(%)" xname="time" yname="heap_ratio"
data={this.state.heapStats} onBrushend={this.props.onBrushend}
data={data.heapStats}
unit="%" unit="%"
scale={{ scale={{
time: { time: {
alias: "时间", alias: "时间",
type: "time", type: "time",
mask: "HH:mm", mask: "HH:mm",
// tickCount: 6, tickCount: 4,
...this.state.timeScale, ...this.props.timeScale,
nice: false, nice: true,
}, },
heap_ratio: { heap_ratio: {
alias: "内存使用百分比", alias: "内存使用百分比",
@ -415,17 +281,17 @@ class StatsCharts extends PureComponent {
<Col xl={12} lg={24} md={24} sm={24} xs={24}> <Col xl={12} lg={24} md={24} sm={24} xs={24}>
<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} onBrushend={this.props.onBrushend}
generateFunc={generateCpuData} data={data.cpuStats}
unit="%" unit="%"
scale={{ scale={{
time: { time: {
alias: "时间", alias: "时间",
type: "time", type: "time",
mask: "HH:mm", mask: "HH:mm",
tickCount: 6, tickCount: 4,
...this.state.timeScale, ...this.props.timeScale,
nice: false, nice: true,
}, },
cpu_ratio: { cpu_ratio: {
alias: "CPU使用百分比", alias: "CPU使用百分比",
@ -442,16 +308,17 @@ class StatsCharts extends PureComponent {
<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}>
<SliderChart title="搜索延迟(ms)" xname="time" yname="latency" <SliderChart title="搜索延迟(ms)" xname="time" yname="latency"
data={this.state.searchLatency} data={data.searchLatency}
onBrushend={this.props.onBrushend}
unit="ms" unit="ms"
scale={{ scale={{
time: { time: {
alias: "时间", alias: "时间",
type: "time", type: "time",
mask: "HH:mm", mask: "HH:mm",
tickCount: 6, tickCount: 4,
...this.state.timeScale, ...this.props.timeScale,
nice: false, nice: true,
}, },
latency: { latency: {
alias: "延迟时长", alias: "延迟时长",
@ -466,17 +333,17 @@ class StatsCharts extends PureComponent {
<Col xl={12} lg={24} md={24} sm={24} xs={24}> <Col xl={12} lg={24} md={24} sm={24} xs={24}>
<SliderChart title="索引延迟(ms)" xname="time" yname="latency" <SliderChart title="索引延迟(ms)" xname="time" yname="latency"
type="index_latency" type="index_latency"
data={this.state.indexLatency} onBrushend={this.props.onBrushend}
generateFunc={generateIndexLatencyData} data={data.indexLatency}
unit="ms" unit="ms"
scale={{ scale={{
time: { time: {
// alias: "时间", // alias: "时间",
type: "time", type: "time",
mask: "HH:mm", mask: "HH:mm",
tickCount: 6, tickCount: 4,
...this.state.timeScale, ...this.props.timeScale,
nice: false, nice: true,
}, },
latency: { latency: {
alias: "延迟时长", alias: "延迟时长",
@ -492,16 +359,18 @@ class StatsCharts extends PureComponent {
<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}>
<SliderChart title="搜索QPS" xname="time" yname="qps" <SliderChart title="搜索QPS" xname="time" yname="qps"
data={this.state.searchQps} type="test"
onBrushend={this.props.onBrushend}
data={data.searchQps}
unit="" unit=""
scale={{ scale={{
time: { time: {
alias: "时间", alias: "时间",
type: "time", type: "time",
mask: "HH:mm", mask: "HH:mm",
tickCount: 6, tickCount: 4,
...this.state.timeScale, ...this.props.timeScale,
nice: false, nice: true,
}, },
qps: { qps: {
min: 0, min: 0,
@ -513,16 +382,17 @@ class StatsCharts extends PureComponent {
</Col> </Col>
<Col xl={12} lg={24} md={24} sm={24} xs={24}> <Col xl={12} lg={24} md={24} sm={24} xs={24}>
<SliderChart title="索引QPS" xname="time" yname="qps" <SliderChart title="索引QPS" xname="time" yname="qps"
data={this.state.indexQps} onBrushend={this.props.onBrushend}
data={data.indexQps}
unit="" unit=""
scale={{ scale={{
time: { time: {
// alias: "时间", // alias: "时间",
type: "time", type: "time",
mask: "HH:mm", mask: "HH:mm",
tickCount: 6, tickCount: 4,
...this.state.timeScale, ...this.props.timeScale,
nice: false, nice: true,
}, },
qps: { qps: {
min: 0, min: 0,
@ -537,16 +407,17 @@ class StatsCharts extends PureComponent {
<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}>
<SliderChart title="索引线程" xname="time" yname="queue" <SliderChart title="索引线程" xname="time" yname="queue"
data={this.state.readThreadQueue} data={data.readThreadQueue}
onBrushend={this.props.onBrushend}
unit="" unit=""
scale={{ scale={{
time: { time: {
alias: "时间", alias: "时间",
type: "time", type: "time",
mask: "HH:mm", mask: "HH:mm",
tickCount: 6, tickCount: 4,
...this.state.timeScale, ...this.props.timeScale,
nice: false, nice: true,
}, },
queue: { queue: {
min: 0, min: 0,
@ -558,16 +429,17 @@ class StatsCharts extends PureComponent {
</Col> </Col>
<Col xl={12} lg={24} md={24} sm={24} xs={24}> <Col xl={12} lg={24} md={24} sm={24} xs={24}>
<SliderChart title="读线程" xname="time" yname="queue" <SliderChart title="读线程" xname="time" yname="queue"
data={this.state.writeThreadQueue} onBrushend={this.props.onBrushend}
data={data.writeThreadQueue}
unit="" unit=""
scale={{ scale={{
time: { time: {
// alias: "时间", // alias: "时间",
type: "time", type: "time",
mask: "HH:mm", mask: "HH:mm",
tickCount: 6, tickCount: 4,
...this.state.timeScale, ...this.props.timeScale,
nice: false, nice: true,
}, },
queue: { queue: {
min: 0, min: 0,
@ -584,23 +456,155 @@ class StatsCharts extends PureComponent {
} }
} }
let startTime = moment();
let fetchDataCount = 0;
let tv1 = null;
@connect(({ clusterMonitor }) => ({ @connect(({ clusterMonitor }) => ({
clusterMonitor clusterMonitor
})) }))
class ClusterMonitor extends PureComponent { 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; 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({ dispatch({
type: 'clusterMonitor/fetchClusterOverview', type: 'clusterMonitor/fetchClusterOverview',
payload: {
timeRange: timeRange,
},
}); });
} }
componentWillUnmount(){
clearInterval(tv1);
}
componentDidMount() { componentDidMount() {
this.fetchData(); this.fetchData();
setInterval(()=>{ // tv1 = setInterval(()=>{
// this.fetchData();
// }, 10000);
this.autoRefresh();
}
autoRefresh(durationInSeconds){
!durationInSeconds && (durationInSeconds = 10);
clearInterval(tv1);
tv1 = setInterval(()=>{
this.fetchData(); 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() { render() {
@ -627,8 +631,64 @@ class ClusterMonitor extends PureComponent {
online_duration: moment.duration(rawStats.nodes.jvm.max_uptime_in_millis).humanize(), online_duration: moment.duration(rawStats.nodes.jvm.max_uptime_in_millis).humanize(),
}; };
} }
//console.log(clusterMonitor.nodes_stats);
const menu = (
<Menu onClick={this.handleQuickSelect}>
<Menu.Item key="1">
最近一小时
</Menu.Item>
<Menu.Item key="2">
最近一天
</Menu.Item>
<Menu.Item key="3">
最近一周
</Menu.Item>
<Menu.Item key="4">
最近一个月
</Menu.Item>
<Menu.Divider/>
<Menu.Item key="5">
<Input.Group compact>
<Button style={{cursor: "default"}}>自动刷新间隔</Button>
<InputNumber min={1} defaultValue={10} ref={el => this.refreshNum = el} />
<Select defaultValue="seconds" ref={el => this.refreshUnit = el}>
<Select.Option value="seconds"></Select.Option>
<Select.Option value="minutes"></Select.Option>
<Select.Option value="hours"></Select.Option>
</Select>
<Button type="primary" onClick={this.handleAutoRefresh}>
确定
</Button>
</Input.Group>
</Menu.Item>
</Menu>
);
return ( return (
<div> <div>
<div style={{background: "#fff", padding: "10px", marginBottom: 5}}>
<Input.Group compact>
<Dropdown overlay={menu}
onVisibleChange={this.handleQSVisibleChange}
visible={this.state.qsVisible}
>
<Button>
快速选择 <Icon type="clock-circle" />
</Button>
</Dropdown>
<RangePicker
showTime={{ format: 'HH:mm' }}
format="YYYY-MM-DD HH:mm"
placeholder={['开始时间', '结束时间']}
defaultValue ={[moment().subtract(1, 'h'), moment()]}
value={this.state.pickerValue}
onChange={this.onTimeChange}
onOk={this.onTimeOk}
/>
<Button type="primary" onClick={this.fetchData}>
刷新
</Button>
</Input.Group>
</div>
<Card style={{marginBottom:5}}> <Card style={{marginBottom:5}}>
<Row> <Row>
<Col md={3} xs={8}> <Col md={3} xs={8}>
@ -663,7 +723,9 @@ class ClusterMonitor extends PureComponent {
</Col> </Col>
</Row> </Row>
</Card> </Card>
<StatsCharts/> <StatsCharts data={clusterMonitor.nodes_stats}
onBrushend={this.handleChartBrush}
timeScale={this.state.timeScale}/>
</div> </div>
); );
} }

View File

@ -6,8 +6,8 @@ export default {
}, },
effects:{ effects:{
*fetchClusterOverview({callback}, {call, put}){ *fetchClusterOverview({payload, callback}, {call, put}){
let clusterData = yield call(getClusterOverview); let clusterData = yield call(getClusterOverview, payload);
yield put({type: 'saveData', payload: clusterData}) yield put({type: 'saveData', payload: clusterData})
if(callback && typeof callback == 'function'){ if(callback && typeof callback == 'function'){
callback(clusterData); callback(clusterData);

View File

@ -1,7 +1,11 @@
import request from '@/utils/request'; import request from '@/utils/request';
export async function getClusterOverview(){ export async function getClusterOverview(payload){
return request('/dashboard/cluster/overview'); return request('/dashboard/cluster/overview',{
method: 'POST',
body: payload,
expirys: 0,
});
} }
export async function getClusterNodeStats(){ export async function getClusterNodeStats(){

View File

@ -123,7 +123,7 @@ export default function request(
} }
return fetch(url, newOptions) return fetch(url, newOptions)
.then(checkStatus) .then(checkStatus)
.then(response => cachedSave(response, hashcode)) // .then(response => cachedSave(response, hashcode))
.then(response => { .then(response => {
// DELETE and 204 do not return data by default // DELETE and 204 do not return data by default
// using .json will report an error. // using .json will report an error.