diff --git a/src/config/locale/en-US/device.json b/src/config/locale/en-US/device.json
index 5fe217d9..1dd3f159 100644
--- a/src/config/locale/en-US/device.json
+++ b/src/config/locale/en-US/device.json
@@ -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"
+ }
}
diff --git a/src/config/locale/zh-CN/device.json b/src/config/locale/zh-CN/device.json
index cffe9362..64624c38 100644
--- a/src/config/locale/zh-CN/device.json
+++ b/src/config/locale/zh-CN/device.json
@@ -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": "已停止"
+ }
}
diff --git a/src/pages/MachineDashboard/index.module.less b/src/pages/MachineDashboard/index.module.less
index 372adcd8..cc51a343 100644
--- a/src/pages/MachineDashboard/index.module.less
+++ b/src/pages/MachineDashboard/index.module.less
@@ -25,7 +25,7 @@
}
.instanceSelect {
- margin-left: 10px;
+ margin-left: 14px;
height: 34px;
:global {
@@ -73,6 +73,7 @@
font-size: 16px;
line-height: 22px;
color: #000000;
+ display: flex;
}
.action {
diff --git a/src/pages/MachineDashboard/index.tsx b/src/pages/MachineDashboard/index.tsx
index fe8b4386..10ca1795 100644
--- a/src/pages/MachineDashboard/index.tsx
+++ b/src/pages/MachineDashboard/index.tsx
@@ -210,9 +210,8 @@ function MachineDashboard(props: IProps) {
-
{intl.get('device.nodeResource.singleNodeTitle')}
-
-
+
+ {intl.get('device.nodeResource.singleNodeTitle')}
{
instanceList.map((instance: string) => (
@@ -221,6 +220,9 @@ function MachineDashboard(props: IProps) {
}
+
+
+
diff --git a/src/pages/ServiceDashboard/OverviewTable.tsx b/src/pages/ServiceDashboard/OverviewTable.tsx
new file mode 100644
index 00000000..349de21b
--- /dev/null
+++ b/src/pages/ServiceDashboard/OverviewTable.tsx
@@ -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,
+ ReturnType {
+ // 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(false);
+ const [frequencyValue, setFrequencyValue] = useState(0);
+ const [dataSource, setDataSource] = useState([]);
+ const pollingTimerRef = useRef(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 -
+ 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 {showText}
+ }
+
+ useEffect(() => {
+ if (cluster?.id) {
+ asyncGetServiceOverviewData(true);
+ }
+ }, [cluster, serviceMap])
+
+ const columns: TableColumnType[] = [
+ {
+ title: intl.get('device.serviceResource.serviceName'),
+ dataIndex: "serviceName",
+ render: (text, _) => {text || '-'}
,
+ },
+ {
+ 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 (
+
+ {isRunning ? intl.get('device.serviceResource.running') : intl.get('device.serviceResource.exit')}
+
+ )
+ },
+ 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 (
+
+ );
+}
+
+export default connect(mapState, mapDispatch)(OverviewTable);
diff --git a/src/pages/ServiceDashboard/ServiceOverview/index.module.less b/src/pages/ServiceDashboard/ServiceOverview/index.module.less
index 7ffd9c3e..c5edfede 100644
--- a/src/pages/ServiceDashboard/ServiceOverview/index.module.less
+++ b/src/pages/ServiceDashboard/ServiceOverview/index.module.less
@@ -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 {
diff --git a/src/pages/ServiceDashboard/ServiceOverview/index.tsx b/src/pages/ServiceDashboard/ServiceOverview/index.tsx
index 2fd35b4b..82fc9235 100644
--- a/src/pages/ServiceDashboard/ServiceOverview/index.tsx
+++ b/src/pages/ServiceDashboard/ServiceOverview/index.tsx
@@ -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,
ReturnType {
serviceType: ServiceName;
- cluster: any;
serviceNames: string[];
panelConfigData: ServicePanelConfig[];
timeRange: TIME_OPTION_TYPE | [number, number];
@@ -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,
@@ -214,4 +218,4 @@ function ServiceOverview(props: IProps) {
)
}
-export default connect(mapState, mapDispatch)(ServiceOverview);;
\ No newline at end of file
+export default connect(mapState, mapDispatch)(ServiceOverview);
\ No newline at end of file
diff --git a/src/pages/ServiceDashboard/defaultPanelConfig.ts b/src/pages/ServiceDashboard/defaultPanelConfig.ts
index 286c8bdb..6f25345f 100644
--- a/src/pages/ServiceDashboard/defaultPanelConfig.ts
+++ b/src/pages/ServiceDashboard/defaultPanelConfig.ts
@@ -107,7 +107,7 @@ export const defaultServicePanelConfigData: ServicePanelConfigItem[] = [
},
{
title: intl.get('metric_description.process_cpu'),
- valueType: VALUE_TYPE.byte,
+ valueType: VALUE_TYPE.percentage,
queries: [
{
refId: 'cpu_seconds_total',
@@ -216,7 +216,7 @@ export const defaultServicePanelConfigData: ServicePanelConfigItem[] = [
},
{
title: intl.get('metric_description.process_cpu'),
- valueType: VALUE_TYPE.byte,
+ valueType: VALUE_TYPE.percentage,
queries: [
{
refId: 'cpu_seconds_total',
@@ -403,7 +403,7 @@ export const defaultServicePanelConfigData: ServicePanelConfigItem[] = [
},
{
title: intl.get('metric_description.process_cpu'),
- valueType: VALUE_TYPE.byte,
+ valueType: VALUE_TYPE.percentage,
queries: [
{
refId: 'cpu_seconds_total',
diff --git a/src/pages/ServiceDashboard/index.module.less b/src/pages/ServiceDashboard/index.module.less
index 2975b13d..c90bd35c 100644
--- a/src/pages/ServiceDashboard/index.module.less
+++ b/src/pages/ServiceDashboard/index.module.less
@@ -26,9 +26,10 @@
}
.singelNodeMonitor {
- background-color: #fff;
- box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.08);
- padding: 16px;
+ // background-color: #fff;
+ // box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.08);
+ // padding: 16px;
+ margin-top: 16px;
}
.singelNodeMonitorHeader {
@@ -50,8 +51,56 @@
align-items: center;
}
+.servicePanel {
+ &:not(:first-child) {
+ margin-top: 16px;
+ }
+}
+
.addPanelBtn {
display: flex;
align-items: center;
margin-left: 10px;
}
+
+.overviewTable {
+ :global {
+ .ant-table-tbody {
+ font-size: 80%;
+ }
+
+ .ant-table-cell{
+ padding: 0;
+ padding-right: 2px;
+ padding-bottom: 1px;
+ }
+
+ .ant-table-thead .ant-table-cell {
+ padding: 16px !important;
+ }
+ }
+
+ .tableCell {
+ width: 100%;
+ height: 100%;
+ padding: 16px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .normal {
+ background-color:rgba(50, 172, 45, 0.97);
+ color: #fff;
+ }
+
+ .warning {
+ background-color:rgba(237, 129, 40, 0.89);
+ color: #fff;
+ }
+
+ .danger {
+ background-color:rgba(245, 54, 54, 0.9);
+ color: #fff;
+ }
+}
\ No newline at end of file
diff --git a/src/pages/ServiceDashboard/index.tsx b/src/pages/ServiceDashboard/index.tsx
index 627257e2..291a2749 100644
--- a/src/pages/ServiceDashboard/index.tsx
+++ b/src/pages/ServiceDashboard/index.tsx
@@ -13,6 +13,7 @@ import { defaultServicePanelConfigData } from './defaultPanelConfig';
import ServiceOverview from './ServiceOverview';
import styles from './index.module.less';
+import OverviewTable from './OverviewTable';
const mapDispatch: any = (_dispatch: any) => ({
});
@@ -36,6 +37,10 @@ const ServicePanels = [
ClusterServiceNameMap[ServiceName.Drainer],
]
+export type ServicePanelType = {
+ [key in typeof ServicePanels[number]]: string[];
+};
+
function ServiceDashboard(props: IProps) {
const { cluster } = props;
@@ -45,7 +50,7 @@ function ServiceDashboard(props: IProps) {
setTimeRange(value);
}
- const [serviceNames, setServiceNames] = useState([]);
+ const [curServiceMap, setCurServiceMap] = useState({});
useEffect(() => {
if (cluster.id) {
@@ -54,20 +59,21 @@ function ServiceDashboard(props: IProps) {
}, [cluster])
const getServiceNames = () => {
- let services: string[] = [];
+ const serviceTypeMap: ServicePanelType = {};
ServicePanels.forEach((panel: string) => {
- services = services.concat(cluster[panel].map(i => i.name))
+ serviceTypeMap[panel] = (cluster[panel] || []).map(i => i.name);
+ // serviceTypeMap = services.concat(cluster[panel].map(i => i.name))
});
- setServiceNames(services);
+ setCurServiceMap(serviceTypeMap);
}
-
-
return (
+ {/* @ts-ignore */}
+
-
{intl.get('device.nodeResource.singleNodeTitle')}
+
{intl.get('device.serviceResource.singleServiceTitle')}
-
s.includes(ServiceName.GRAPHD))}
- timeRange={timeRange}
- serviceType={ServiceName.GRAPHD}
- panelVisible
- panelConfigData={defaultServicePanelConfigData.find(item => item.type === ServiceName.GRAPHD)?.panels || []}
- />
- s.includes(ServiceName.METAD))}
- timeRange={timeRange}
- serviceType={ServiceName.METAD}
- panelConfigData={defaultServicePanelConfigData.find(item => item.type === ServiceName.METAD)?.panels || []}
- />
- s.includes(ServiceName.STORAGED))}
- timeRange={timeRange}
- serviceType={ServiceName.STORAGED}
- panelConfigData={defaultServicePanelConfigData.find(item => item.type === ServiceName.STORAGED)?.panels || []}
- />
+
+ item.type === ServiceName.GRAPHD)?.panels || []}
+ />
+
+
+ item.type === ServiceName.METAD)?.panels || []}
+ />
+
+
+ item.type === ServiceName.STORAGED)?.panels || []}
+ />
+
);
}
diff --git a/src/utils/promQL.ts b/src/utils/promQL.ts
index d49582e0..51175040 100644
--- a/src/utils/promQL.ts
+++ b/src/utils/promQL.ts
@@ -300,19 +300,19 @@ export const getMachineMetricData = (instance, cluster) => {
queries: [
{
refId: 'cpu_total_used',
- query: `(1 - avg(rate(node_cpu_seconds_total{mode="idle"${clusterSuffix1},${instanceSuffix}}[30s])) by (instance))*100`,
+ query: `(1 - avg(irate(node_cpu_seconds_total{mode="idle"${clusterSuffix1},${instanceSuffix}}[30s])) by (instance))*100`,
},
{
refId: 'cpu_system_used',
- query: `avg(rate(node_cpu_seconds_total{mode="system"${clusterSuffix1},${instanceSuffix}}[30s])) by (instance) *100`,
+ query: `avg(irate(node_cpu_seconds_total{mode="system"${clusterSuffix1},${instanceSuffix}}[30s])) by (instance) *100`,
},
{
refId: 'cpu_user_used',
- query: `avg(rate(node_cpu_seconds_total{mode="user"${clusterSuffix1},${instanceSuffix}}[30s])) by (instance) *100`,
+ query: `avg(irate(node_cpu_seconds_total{mode="user"${clusterSuffix1},${instanceSuffix}}[30s])) by (instance) *100`,
},
{
refId: 'cpu_io_wait_used',
- query: `avg(rate(node_cpu_seconds_total{mode="iowait"${clusterSuffix1},${instanceSuffix}}[30s])) by (instance) *100`,
+ query: `avg(irate(node_cpu_seconds_total{mode="iowait"${clusterSuffix1},${instanceSuffix}}[30s])) by (instance) *100`,
}
]
},