Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update service monitor overview #230

Merged
merged 1 commit into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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