Skip to content

Commit

Permalink
feat: update service monitor overview (#230)
Browse files Browse the repository at this point in the history
  • Loading branch information
xigongdaEricyang authored Apr 7, 2023
1 parent f3b3a2e commit c53750e
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 49 deletions.
15 changes: 14 additions & 1 deletion src/config/locale/en-US/device.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,18 @@
"singleNodeTitle": "Single Node Monitor",
"waterLevel": "Resrouce water level",
"core": "Core"
}
},
"serviceResource": {
"singleServiceTitle": "Single Service Monitor",
"serviceName": "Service Name",
"context_switches_total": "Context Switch",
"cpu_seconds_total": "CPU Seconds",
"memory_bytes_gauge": "Memory Used",
"read_bytes_total": "Read Bytes",
"write_bytes_total": "Write Bytes",
"open_filedesc_gauge": "Open Files",
"process_count": "Status",
"running": "Running",
"exit": "Exited"
}
}
15 changes: 14 additions & 1 deletion src/config/locale/zh-CN/device.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,18 @@
"singleNodeTitle": "单节点监控",
"waterLevel": "资源水位",
"core": ""
}
},
"serviceResource": {
"singleServiceTitle": "单服务监控",
"serviceName": "服务名",
"context_switches_total": "上下文切换",
"cpu_seconds_total": "CPU使用时间",
"memory_bytes_gauge": "内存使用量",
"read_bytes_total": "磁盘读取",
"write_bytes_total": "磁盘写入",
"open_filedesc_gauge": "文件句柄数",
"process_count": "服务状态",
"running": "运行中",
"exit": "已停止"
}
}
3 changes: 2 additions & 1 deletion src/pages/MachineDashboard/index.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
}

.instanceSelect {
margin-left: 10px;
margin-left: 14px;
height: 34px;

:global {
Expand Down Expand Up @@ -73,6 +73,7 @@
font-size: 16px;
line-height: 22px;
color: #000000;
display: flex;
}

.action {
Expand Down
8 changes: 5 additions & 3 deletions src/pages/MachineDashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,8 @@ function MachineDashboard(props: IProps) {
</div>
<div className={styles.singelNodeMonitor}>
<div className={styles.singelNodeMonitorHeader}>
<div className={styles.monitorTitle}>{intl.get('device.nodeResource.singleNodeTitle')}</div>
<div className={styles.action}>
<TimeSelect value={timeRange} onChange={handleTimeSelectChange} />
<div className={styles.monitorTitle}>
{intl.get('device.nodeResource.singleNodeTitle')}
<DashboardSelect className={styles.instanceSelect} value={curInstance} onChange={handleInstanceChange}>
{
instanceList.map((instance: string) => (
Expand All @@ -221,6 +220,9 @@ function MachineDashboard(props: IProps) {
}
</DashboardSelect>
</div>
<div className={styles.action}>
<TimeSelect value={timeRange} onChange={handleTimeSelectChange} />
</div>
</div>
<Spin spinning={singleNodeLoading}>
<Row>
Expand Down
253 changes: 253 additions & 0 deletions src/pages/ServiceDashboard/OverviewTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import React, { useEffect, useRef, useState } from 'react';
import { Table, TableColumnType } from 'antd';
import intl from 'react-intl-universal';

import { ServicePanelType } from './index';

import styles from './index.module.less';
import { BatchQueryItem, ServiceName } from '@/utils/interface';
import { connect } from 'react-redux';
import { getClusterPrefix, VALUE_TYPE } from '@/utils/promQL';
import { asyncBatchQueries } from '@/requests';
import { getAutoLatency, getProperByteDesc } from '@/utils/dashboard';
import EventBus from '@/utils/EventBus';

interface OverviewTableData {
serviceName: string;
}

const metrics = [
'context_switches_total',
'cpu_seconds_total',
'memory_bytes_gauge',
'read_bytes_total',
'write_bytes_total',
'open_filedesc_gauge',
'count',
]

const mapDispatch: any = (_dispatch: any) => ({
});

const mapState = (state: any) => ({
serviceMetric: state.serviceMetric,
ready: state.serviceMetric.ready,
cluster: state.cluster.cluster
});

interface IProps
extends ReturnType<typeof mapDispatch>,
ReturnType<typeof mapState> {
// resourceInfos: NodeResourceInfo[];
// loading: boolean;
serviceMap: ServicePanelType;
}

const CellWidth = 150;

const ShowedServices: string[] = [ServiceName.GRAPHD, ServiceName.METAD, ServiceName.STORAGED];

function OverviewTable(props: IProps) {

const { serviceMap, serviceMetric, cluster } = props;
const [loading, setLoading] = useState<boolean>(false);
const [frequencyValue, setFrequencyValue] = useState<number>(0);
const [dataSource, setDataSource] = useState<OverviewTableData[]>([]);
const pollingTimerRef = useRef<any>(null);

useEffect(() => {
const changeListener = (data) => {
const { value } = data.detail;
setFrequencyValue(value);
}
const freshListener = () => { handleRefresh(); }
EventBus.getInstance().on('serviceOverview_change', changeListener);

EventBus.getInstance().on('serviceOverview_fresh', freshListener);

return () => {
EventBus.getInstance().off('serviceOverview_change', changeListener);
EventBus.getInstance().off('serviceOverview_fresh', freshListener);
}
}, [cluster, serviceMap]);

useEffect(() => {
if (pollingTimerRef.current) {
clearPolling();
}
if (frequencyValue > 0) {
pollingData();
}
}, [frequencyValue])

const handleRefresh = () => {
asyncGetServiceOverviewData(true);
}

const clearPolling = () => {
if (pollingTimerRef.current) {
clearInterval(pollingTimerRef.current);
}
};

const pollingData = () => {
asyncGetServiceOverviewData(false);
if (frequencyValue > 0) {
pollingTimerRef.current = setInterval(() => {
asyncGetServiceOverviewData(false);
}, frequencyValue);
}
}

const renderCell = (text: string, type: VALUE_TYPE = VALUE_TYPE.byte) => {
if (!text) return <div className={`${styles.tableCell}`}>-</div>
let showText: string = '';
switch (type) {
case VALUE_TYPE.number:
showText = text;
break;
case VALUE_TYPE.percentage:
showText = `${text}%`;
break;
case VALUE_TYPE.byte:
showText = getProperByteDesc(parseInt(text)).desc;
break;
case VALUE_TYPE.latency:
showText = getAutoLatency(parseInt(text));
break;
default:
break;
}
return <div className={`${styles.tableCell}`}>{showText}</div>
}

useEffect(() => {
if (cluster?.id) {
asyncGetServiceOverviewData(true);
}
}, [cluster, serviceMap])

const columns: TableColumnType<OverviewTableData>[] = [
{
title: intl.get('device.serviceResource.serviceName'),
dataIndex: "serviceName",
render: (text, _) => <div className={styles.tableCell}>{text || '-'}</div>,
},
{
title: intl.get('device.serviceResource.context_switches_total'),
dataIndex: "context_switches_total",
render: (text, _) => renderCell(text, VALUE_TYPE.number),
width: CellWidth
},
{
title: intl.get('device.serviceResource.cpu_seconds_total'),
dataIndex: "cpu_seconds_total",
render: (text, _) => renderCell(text, VALUE_TYPE.percentage),
width: CellWidth
},
{
title: intl.get('device.serviceResource.memory_bytes_gauge'),
dataIndex: "memory_bytes_gauge",
render: (text, _) => renderCell(text),
width: CellWidth
},
{
title: intl.get('device.serviceResource.read_bytes_total'),
dataIndex: "read_bytes_total",
ellipsis: true,
render: (text, _) => renderCell(text),
width: CellWidth
},
{
title: intl.get('device.serviceResource.write_bytes_total'),
dataIndex: "write_bytes_total",
ellipsis: true,
render: (text, _) => renderCell(text),
width: CellWidth
},
{
title: intl.get('device.serviceResource.open_filedesc_gauge'),
dataIndex: "open_filedesc_gauge",
ellipsis: true,
render: (text, _) => renderCell(text, VALUE_TYPE.number),
width: CellWidth
},
{
title: intl.get('device.serviceResource.process_count'),
dataIndex: "count",
ellipsis: true,
render: (text, _) => {
const isRunning = text === '1';
return (
<div className={`${styles.tableCell} ${isRunning ? styles.normal : styles.danger}`}>
{isRunning ? intl.get('device.serviceResource.running') : intl.get('device.serviceResource.exit')}
</div>
)
},
width: CellWidth
},
]

const getQueries = () => {
if (!cluster?.id) return [];
const clusterSuffix1 = `{${getClusterPrefix()}="${cluster.id}"}`;
const queries: BatchQueryItem[] = [];
ShowedServices.forEach((service) => {
metrics.forEach((metric) => {
let query = `nebula_${service}_${metric}${clusterSuffix1} - 0`;
if (metric === 'cpu_seconds_total') {
query = `avg by (instanceName) (irate(nebula_${service}_${metric}${clusterSuffix1}[30s])) * 100`
}
queries.push({
refId: `${service}$${metric}`,
query,
})
})
})
return queries;
}

const asyncGetServiceOverviewData = async (shouldLoading?: boolean) => {
if (!serviceMap[ServiceName.GRAPHD]) return;
shouldLoading && setLoading(true);
const queries = getQueries();
const data: any = await asyncBatchQueries(queries);
const { results } = data;
const curDataSources: OverviewTableData[] = serviceMap[ServiceName.GRAPHD]
.concat(serviceMap[ServiceName.METAD])
.concat(serviceMap[ServiceName.STORAGED])
.map(item => ({ serviceName: item }));
Object.keys(results).forEach(refId => {
const [_serviceType, metricName] = refId.split('$');
const metricItems = results[refId].result;
metricItems.forEach(({ metric, value }) => {
const curItem = curDataSources.find(item => item.serviceName === metric.instanceName);
if (curItem) {
curItem[metricName] = value[1];
} else {
curDataSources.push({
serviceName: metric.instanceName,
[metricName]: value[1]
})
}
})
});
setLoading(false);
setDataSource(curDataSources);
}

return (
<Table
dataSource={dataSource}
rowKey="serviceName"
loading={loading}
columns={columns}
pagination={{
hideOnSinglePage: true,
}}
className={styles.overviewTable}
/>
);
}

export default connect(mapState, mapDispatch)(OverviewTable);
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// border: 1px solid #D9D9D9;
background-color: #fff;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.08);
margin-top: 24px;
// margin-top: 24px;
}

.headerTitle {
Expand Down
10 changes: 7 additions & 3 deletions src/pages/ServiceDashboard/ServiceOverview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ const mapDispatch: any = (_dispatch: any) => ({
const mapState = (state: any) => ({
serviceMetric: state.serviceMetric,
ready: state.serviceMetric.ready,
cluster: state.cluster.cluster
});

interface IProps extends ReturnType<typeof mapDispatch>,
ReturnType<typeof mapState> {
serviceType: ServiceName;
cluster: any;
serviceNames: string[];
panelConfigData: ServicePanelConfig[];
timeRange: TIME_OPTION_TYPE | [number, number];
Expand Down Expand Up @@ -110,7 +110,11 @@ function ServiceOverview(props: IProps) {
if (aggregation === AggregationType.Sum && !metricItem.isRawMetric) {
query = `sum_over_time(${query}{instanceName="${curServiceName}"${clusterSuffix1}}[${15}s])`;
} else {
query = `${query}{instanceName="${curServiceName}"${clusterSuffix1}}`;
if (query.includes('cpu_seconds_total')) {
query = `avg by (instanceName) (irate(${query}{instanceName="${curServiceName}"${clusterSuffix1}}[30s])) * 100`
} else {
query = `${query}{instanceName="${curServiceName}"${clusterSuffix1}}`;
}
}
return {
refId: queryItem.query,
Expand Down Expand Up @@ -214,4 +218,4 @@ function ServiceOverview(props: IProps) {
)
}

export default connect(mapState, mapDispatch)(ServiceOverview);;
export default connect(mapState, mapDispatch)(ServiceOverview);
Loading

0 comments on commit c53750e

Please sign in to comment.