add time range query
This commit is contained in:
parent
c478823d4c
commit
0bbbc66182
|
@ -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());
|
||||||
|
}
|
|
@ -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({
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(){
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue