add cluster list page

This commit is contained in:
silenceqi 2020-12-17 21:23:54 +08:00
parent 0bbbc66182
commit a8a4fb9de6
8 changed files with 5995 additions and 597 deletions

View File

@ -30,9 +30,14 @@ export default [
name: 'gateway',
component: './Dashboard/GatewayMonitor',
}, {
path: '/platform/cluster',
path: '/platform/cluster/:name',
name: 'cluster',
component: './Dashboard/ClusterMonitor',
hideInMenu: true,
}, {
path: '/platform/clusterlist',
name: 'cluster',
component: './Dashboard/ClusterList',
}, {
path: '/platform/tasks',
name: 'tasks',

View File

@ -5,555 +5,8 @@ import { promises } from 'dns';
//import {formatTimestampToDuration} from './format_timestamp_to_duration';
const minIntervalSeconds = 10;
let data = JSON.parse(`{
"cluster_stats" : {
"indices" : {
"completion" : {
"size_in_bytes" : 0
},
"shards" : {
"replication" : 0,
"primaries" : 11,
"total" : 11,
"index" : {
"replication" : {
"avg" : 0,
"min" : 0,
"max" : 0
},
"shards" : {
"min" : 1,
"avg" : 1,
"max" : 1
},
"primaries" : {
"avg" : 1,
"min" : 1,
"max" : 1
}
}
},
"mappings" : {
"field_types" : [
{
"name" : "alias",
"count" : 3,
"index_count" : 1
},
{
"name" : "binary",
"count" : 9,
"index_count" : 1
},
{
"name" : "boolean",
"count" : 101,
"index_count" : 6
},
{
"name" : "byte",
"count" : 1,
"index_count" : 1
},
{
"name" : "date",
"count" : 136,
"index_count" : 10
},
{
"name" : "double",
"count" : 103,
"index_count" : 1
},
{
"count" : 9,
"name" : "flattened",
"index_count" : 1
},
{
"name" : "float",
"count" : 142,
"index_count" : 2
},
{
"name" : "geo_point",
"count" : 7,
"index_count" : 1
},
{
"name" : "half_float",
"count" : 10,
"index_count" : 2
},
{
"name" : "integer",
"count" : 71,
"index_count" : 4
},
{
"count" : 20,
"name" : "ip",
"index_count" : 1
},
{
"name" : "keyword",
"count" : 1312,
"index_count" : 10
},
{
"name" : "long",
"count" : 2623,
"index_count" : 8
},
{
"name" : "nested",
"count" : 14,
"index_count" : 5
},
{
"name" : "object",
"count" : 2724,
"index_count" : 10
},
{
"count" : 121,
"name" : "scaled_float",
"index_count" : 1
},
{
"name" : "text",
"count" : 201,
"index_count" : 9
}
]
},
"query_cache" : {
"miss_count" : 31874,
"cache_size" : 0,
"memory_size_in_bytes" : 0,
"total_count" : 31874,
"evictions" : 0,
"hit_count" : 0,
"cache_count" : 0
},
"docs" : {
"deleted" : 692,
"count" : 102865
},
"fielddata" : {
"memory_size_in_bytes" : 1760,
"evictions" : 0
},
"count" : 11,
"store" : {
"size_in_bytes" : 5.2982999E7,
"reserved_in_bytes" : 0
},
"analysis" : {
"built_in_filters" : [ ],
"built_in_tokenizers" : [ ],
"tokenizer_types" : [ ],
"analyzer_types" : [ ],
"filter_types" : [ ],
"char_filter_types" : [ ],
"built_in_char_filters" : [ ],
"built_in_analyzers" : [ ]
},
"segments" : {
"version_map_memory_in_bytes" : 0,
"norms_memory_in_bytes" : 2496,
"file_sizes" : { },
"max_unsafe_auto_id_timestamp" : -1,
"count" : 36,
"fixed_bit_set_memory_in_bytes" : 14048,
"term_vectors_memory_in_bytes" : 0,
"points_memory_in_bytes" : 0,
"index_writer_memory_in_bytes" : 0,
"memory_in_bytes" : 332776,
"terms_memory_in_bytes" : 109824,
"doc_values_memory_in_bytes" : 202632,
"stored_fields_memory_in_bytes" : 17824
}
},
"nodes" : {
"jvm" : {
"max_uptime_in_millis" : 3.1722515E7,
"mem" : {
"heap_max_in_bytes" : 1.073741824E9,
"heap_used_in_bytes" : 3.3389824E8
},
"versions" : [
{
"vm_version" : "15.0.1+9",
"using_bundled_jdk" : true,
"bundled_jdk" : true,
"count" : 1,
"vm_vendor" : "AdoptOpenJDK",
"version" : "15.0.1",
"vm_name" : "OpenJDK 64-Bit Server VM"
}
],
"threads" : 86
},
"process" : {
"open_file_descriptors" : {
"avg" : 402,
"min" : 402,
"max" : 402
},
"cpu" : {
"percent" : 0
}
},
"network_types" : {
"http_types" : {
"security4" : 1
},
"transport_types" : {
"security4" : 1
}
},
"os" : {
"available_processors" : 8,
"pretty_names" : [
{
"pretty_name" : "Mac OS X",
"count" : 1
}
],
"names" : [
{
"name" : "Mac OS X",
"count" : 1
}
],
"mem" : {
"used_in_bytes" : 1.6490479616E10,
"free_percent" : 4,
"total_in_bytes" : 1.7179869184E10,
"free_in_bytes" : 6.89389568E8,
"used_percent" : 96
},
"allocated_processors" : 8
},
"versions" : [
"7.10.0"
],
"discovery_types" : {
"zen" : 1
},
"plugins" : [ ],
"count" : {
"data_warm" : 1,
"data" : 1,
"data_content" : 1,
"coordinating_only" : 0,
"ingest" : 1,
"master" : 1,
"transform" : 1,
"total" : 1,
"remote_cluster_client" : 1,
"data_cold" : 1,
"voting_only" : 0,
"ml" : 1,
"data_hot" : 1
},
"packaging_types" : [
{
"flavor" : "default",
"count" : 1,
"type" : "tar"
}
],
"fs" : {
"total_in_bytes" : 1.000240963584E12,
"free_in_bytes" : 9.55349225472E11,
"available_in_bytes" : 9.39232976896E11
},
"ingest" : {
"number_of_pipelines" : 1,
"processor_stats" : {
"gsub" : {
"current" : 0,
"time_in_millis" : 0,
"count" : 0,
"failed" : 0
},
"script" : {
"current" : 0,
"time_in_millis" : 0,
"count" : 0,
"failed" : 0
}
}
}
},
"cluster_uuid" : "JFpIbacZQamv9hkgQEDZ2Q",
"timestamp" : 1.606981605839E12,
"status" : "yellow"
}
}`);
let nodesStats = JSON.parse(`[
{
"key" : "node-1",
"doc_count" : 11,
"metrics" : {
"buckets" : [
{
"key_as_string" : "2020-12-04T09:40:00.000Z",
"key" : 1607074800000,
"doc_count" : 1,
"heap_percent" : {
"value" : 54.0
},
"heap_used" : {
"value" : 5.84281136E8
},
"index_time" : {
"value" : 5935.0
},
"cpu_used" : {
"value" : 0.0
},
"search_query_total" : {
"value" : 39044.0
},
"index_total" : {
"value" : 5130.0
},
"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
}
},
{
"key_as_string" : "2020-12-04T09:40:30.000Z",
"key" : 1607074830000,
"doc_count" : 3,
"heap_percent" : {
"value" : 63.0
},
"heap_used" : {
"value" : 6.776044E8
},
"index_time" : {
"value" : 6037.0
},
"cpu_used" : {
"value" : 1.0
},
"search_query_total" : {
"value" : 39121.0
},
"index_total" : {
"value" : 5285.0
},
"search_query_time" : {
"value" : 7328.0
},
"ds" : {
"value" : 77.0
},
"ds1" : {
"value" : 14.0
},
"ds3" : {
"value" : 155.0
},
"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
}
},
{
"key_as_string" : "2020-12-04T09:41:00.000Z",
"key" : 1607074860000,
"doc_count" : 3,
"heap_percent" : {
"value" : 64.0
},
"heap_used" : {
"value" : 6.92956392E8
},
"index_time" : {
"value" : 6181.0
},
"cpu_used" : {
"value" : 1.0
},
"search_query_total" : {
"value" : 39231.0
},
"index_total" : {
"value" : 5426.0
},
"search_query_time" : {
"value" : 7343.0
},
"ds" : {
"value" : 110.0
},
"ds1" : {
"value" : 15.0
},
"ds3" : {
"value" : 141.0
},
"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
}
},
{
"key_as_string" : "2020-12-04T09:41:30.000Z",
"key" : 1607074890000,
"doc_count" : 3,
"heap_percent" : {
"value" : 64.0
},
"heap_used" : {
"value" : 6.9177856E8
},
"index_time" : {
"value" : 6304.0
},
"cpu_used" : {
"value" : 1.0
},
"search_query_total" : {
"value" : 39339.0
},
"index_total" : {
"value" : 5582.0
},
"search_query_time" : {
"value" : 7358.0
},
"ds" : {
"value" : 108.0
},
"ds1" : {
"value" : 15.0
},
"ds3" : {
"value" : 156.0
},
"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
}
},
{
"key_as_string" : "2020-12-04T09:42:00.000Z",
"key" : 1607074920000,
"doc_count" : 1,
"heap_percent" : {
"value" : 16.0
},
"heap_used" : {
"value" : 1.81371952E8
},
"index_time" : {
"value" : 6323.0
},
"cpu_used" : {
"value" : 1.0
},
"search_query_total" : {
"value" : 39375.0
},
"index_total" : {
"value" : 5631.0
},
"search_query_time" : {
"value" : 7361.0
},
"ds" : {
"value" : 36.0
},
"ds1" : {
"value" : 3.0
},
"ds3" : {
"value" : 49.0
},
"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
}
}
]
}
}
]`);
import {clusterData, clusterList} from './data/cluster';
function getOverviewBody(params){
let body = {
_source: [ "cluster_stats"],
@ -798,25 +251,20 @@ function getNodesStats(params){
export default {
'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);
//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);
// });
res.send(clusterData);
},
'GET /dashboard/cluster/nodes_stats': function(req, res) {
let min = moment(1607839878669 - 2592000000).valueOf();
@ -836,12 +284,8 @@ function getNodesStats(params){
}).catch(err=>{
console.log(err);
});
// getNodesStats().then((rdata)=>{
// res.send({
// nodes_stats: rdata
// });
// }).catch(err=>{
// console.log(err);
// })
},
'GET /dashboard/cluster/list': function(req, res){
res.send(clusterList);
}
};

File diff suppressed because it is too large Load Diff

View File

@ -88,6 +88,7 @@ export default {
'menu.platform.cluster': '集群监控',
'menu.platform.tasks': '任务监控',
'menu.platform.search': '搜索监控',
'menu.platform.clusterlist': '集群列表',
'menu.data': '数据管理',

View File

@ -0,0 +1,124 @@
import React, { Component } from 'react';
import {List,Card,Tag} from 'antd';
import {Link} from 'umi';
import {extendHex, defineGrid} from 'honeycomb-grid';
import { SVG } from '@svgdotjs/svg.js'
import { connect } from 'dva';
@connect(({ clusterMonitor }) => ({
clusterMonitor
}))
class ClusterList extends React.Component{
componentDidMount(){
let {dispatch} = this.props;
dispatch({
type: 'clusterMonitor/fetchClusterList',
});
}
render(){
let data = this.props.clusterMonitor.clusterList || [];
return (
<div>
<List
grid={{
gutter: 8,
xs: 1,
sm: 2,
md: 2,
lg: 2,
xl: 3,
xxl: 3,
}}
dataSource={data}
renderItem={item => (
<List.Item key={item.name}>
<Card title={item.name + ": "+ item.nodes.length}
extra={<Link to={"/platform/cluster/" + item.name}>查看更多</Link>}
>
<div>
{/* {item.nodes.map(node => {
return (<a><Tag style={{marginBottom:5}} color={node.status}>{node.name}</Tag></a>);
})} */}
<ClusterItem data={item} />
</div>
</Card>
</List.Item>
)}
/>
</div>
);
}
}
class ClusterItem extends Component {
componentDidMount(){
this.root.addEventListener("click", function({offsetX, offsetY}){
const hexCoordinates = Grid.pointToHex(offsetX, offsetY)
let hex = grid.get(hexCoordinates);
if(!hex || !hex.polygon){
return
}
//console.log(hex.cartesian());
})
const draw = SVG().addTo(this.root).size('100%', '100%')
let data = this.props.data;
const rect = { width: 5, height: 3 };
const Hex = extendHex({
size: 30,
render(draw, idx) {
const position = this.toPoint()
const centerPosition = this.center().add(position)
this.draw = draw
this.group = this.draw.group()
this.group.mouseenter(() => {
//this.lastStroke = this.polygon.attr('stroke');
this.polygon.attr({stroke: '#fff'});
// this.lastFill = this.polygon.attr('fill');
// this.polygon.attr({fill: '#1890FF'})
}).mouseleave(()=>{
//this.polygon.attr({fill: this.lastFill})
this.polygon.attr('stroke', '#999')
}) //.css('cursor', 'pointer')
this.polygon = this.group
.polygon(this.corners().map(({ x, y }) => `${x},${y}`))
.fill(data.nodes[idx-1].status)
.stroke({ width: 1, color: '#999' })
.translate(position.x, position.y)
const fontSize = 12
this.group
.text(data.nodes[idx-1].name)
.font({
size: fontSize,
anchor: 'middle',
leading: 1.4,
fill: '#ccc'
})
.translate(centerPosition.x, centerPosition.y - fontSize)
}
})
const Grid = defineGrid(Hex)
let grid = Grid.rectangle({
...rect,
onCreate: (hex) => {
const { x, y } = hex.toPoint()
let idx = (hex.y * rect.width + hex.x + 1);
if( idx > data.nodes.length){
return
}
hex.render(draw, idx)
}
})
}
render(){
return (
<div ref={ref=>this.root=ref}></div>
)
}
}
export default ClusterList;

View File

@ -86,6 +86,9 @@ import {
}}
onPlotLeave={ev=>{
charts.forEach(chart=>{
if(!chart.get('tooltipController')){
return;
}
chart.hideTooltip();
});
}}
@ -496,8 +499,10 @@ class ClusterMonitor extends PureComponent {
//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){
}else if (msDiff > 1000 * 3600 * 24 * 5 && msDiff <= 1000 * 3600 * 24 * 182){
timeMask = 'MM-DD'
}else if(msDiff > 1000 * 3600 * 24 * 182){
timeMask = 'YY-MM-DD'
}
this.setState({timeScale: {min: timeRange.min, max: timeRange.max, mask: timeMask}});
dispatch({
@ -511,7 +516,23 @@ class ClusterMonitor extends PureComponent {
clearInterval(tv1);
}
componentDidMount() {
this.fetchData();
const { match, location } = this.props;
console.log(location.query.name, match.params);
let min = location.query.start || '2020-12-10 15:00';
let max = location.query.end || '2020-12-10 16:00';
min = moment(min, 'YYYY-MM-DD HH:mm');
max = moment(max, 'YYYY-MM-DD HH:mm');
this.setState({
timeRange:{
min: min,
max: max,
},
lastSeconds: 0,
pickerValue: [min, max],
},()=>{
this.fetchData();
})
// tv1 = setInterval(()=>{
// this.fetchData();
// }, 10000);
@ -548,7 +569,8 @@ class ClusterMonitor extends PureComponent {
handleQuickSelect = (ev) =>{
let lastSeconds = 0;
switch(ev.key){
let key = ev.key || ev.target.type;
switch(key){
case "2":
lastSeconds = 3600 * 24;
break;
@ -559,7 +581,11 @@ class ClusterMonitor extends PureComponent {
lastSeconds = 3600 * 24 * 30;
break;
case "5":
return;
lastSeconds = 3600 * 24 * 30 * 3;
break;
case "6":
lastSeconds = 3600 * 24 * 365;
break;
default:
lastSeconds = 60 * 60;
}
@ -607,6 +633,37 @@ class ClusterMonitor extends PureComponent {
this.autoRefresh(durationInSeconds);
}
handleRecentInput = ()=>{
let unit = this.recentUnit.rcSelect.state.value[0];
let base = 1;
switch(unit){
case "minutes":
base *= 60;
break;
case "hours":
base *= 3600;
break;
case "days":
base *= 3600 * 24;
break;
case "weeks":
base *= 3600 * 24 * 7;
break;
case "months":
base *= 3600 * 24 * 30;
break;
case "years":
base *= 3600 * 24 * 365;
break;
}
let lastSeconds = this.recentNum.inputNumberRef.state.value * base;
this.setState({
lastSeconds: lastSeconds,
},()=>{
this.fetchData();
});
}
render() {
let vstyle = {
fontSize: 16,
@ -633,21 +690,51 @@ class ClusterMonitor extends PureComponent {
}
//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">
<div style={{background:"#fff", border: "1px solid #ccc", padding: 10}}>
<Input.Group compact style={{marginBottom: 10}}>
<Button style={{cursor: "default"}}>最近</Button>
<InputNumber min={1} defaultValue={1} ref={el => this.recentNum = el} />
<Select defaultValue="hours" ref={el => this.recentUnit = el}>
<Select.Option value="minutes"></Select.Option>
<Select.Option value="hours"></Select.Option>
<Select.Option value="days"></Select.Option>
<Select.Option value="weeks"></Select.Option>
<Select.Option value="months"></Select.Option>
<Select.Option value="years"></Select.Option>
</Select>
<Button type="primary" onClick={this.handleRecentInput}>
确定
</Button>
</Input.Group>
<div style={{marginBottom: 10}}>
<Row gutter={[24, 5]}>
<Col span={12}><a type="1" onClick={this.handleQuickSelect}>最近一个小时</a></Col>
<Col span={12}><a type="2" onClick={this.handleQuickSelect}>最近一天</a></Col>
</Row>
<Row gutter={[24,5]}>
<Col span={12}><a type="3" onClick={this.handleQuickSelect}>最近一周</a></Col>
<Col span={12}><a type="4" onClick={this.handleQuickSelect}>最近一个月</a></Col>
</Row>
<Row gutter={[24,5]}>
<Col span={12}><a type="5" onClick={this.handleQuickSelect}>最近三个月</a></Col>
<Col span={12}><a type="6" onClick={this.handleQuickSelect}>最近一年</a></Col>
</Row>
</div>
{/* // <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} />
@ -660,8 +747,9 @@ class ClusterMonitor extends PureComponent {
确定
</Button>
</Input.Group>
</Menu.Item>
</Menu>
{/* // </Menu.Item>
// </Menu> */}
</div>
);
return (
<div>

View File

@ -1,4 +1,4 @@
import {getClusterOverview, getClusterNodeStats} from "@/services/dashboard";
import {getClusterOverview, getClusterNodeStats, getClusterList} from "@/services/dashboard";
export default {
namespace: 'clusterMonitor',
@ -19,6 +19,15 @@ export default {
if(callback && typeof callback == 'function'){
callback(nodesStats);
}
},
*fetchClusterList({callback}, {call, put}){
let clusterData = yield call(getClusterList);
yield put({type: 'saveData', payload: {
clusterList: clusterData
}})
if(callback && typeof callback == 'function'){
callback(clusterData);
}
}
},
reducers:{

View File

@ -1,4 +1,5 @@
import request from '@/utils/request';
import { func } from 'prop-types';
export async function getClusterOverview(payload){
return request('/dashboard/cluster/overview',{
@ -10,4 +11,8 @@ export async function getClusterOverview(payload){
export async function getClusterNodeStats(){
return request('/dashboard/cluster/nodes_stats');
}
export async function getClusterList(){
return request('/dashboard/cluster/list');
}