feat: 添加缓存监控

This commit is contained in:
2025-02-12 00:32:16 +08:00
parent 3f10ae515d
commit cf1d50f1de
2 changed files with 290 additions and 0 deletions

View File

@@ -1,6 +1,7 @@
import { http } from "@/utils/http";
import type {
OperationLogInfo,
RedisMonitorInfo,
SystemMonitorInfo,
UserLoginLogInfo
} from "types/monitor";
@@ -59,3 +60,13 @@ export const getUserOperationsAPI = (params: {
export const getSystemMonitorInfoAPI = () => {
return http.request<SystemMonitorInfo>("get", "/api/server");
};
// --------------------------缓存相关--------------------------------------
/**
* 获取服务器缓存信息
* @returns
*/
export const getCachedMonitorInfoAPI = () => {
return http.request<RedisMonitorInfo>("get", "/api/cache/monitor");
};

279
src/views/monitor/cache/index.vue vendored Normal file
View File

@@ -0,0 +1,279 @@
<template>
<div class="main">
<el-row>
<re-col :value="24" :xs="24" :sm="24">
<el-card shadow="never">
<template #header>
<div class="flex items-center justify-center">
<IconifyIconOffline :icon="Monitor" />
<span class="header-title">基本信息</span>
</div>
</template>
<el-descriptions :column="4" border class="centered-descriptions">
<el-descriptions-item align="center" label="Redis版本" :span="1">
{{ cache.info?.redis_version || "-" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="运行模式" :span="1">
{{ cache.info?.redis_mode === "standalone" ? "单机" : "集群" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="端口" :span="1">
{{ cache.info?.tcp_port || "-" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="客户端数" :span="1">
{{ cache.info?.connected_clients || "-" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="运行时间(天)" :span="1">
{{ cache.info?.uptime_in_days || "-" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="使用内存" :span="1">
{{ cache.info?.used_memory_human || "-" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="使用CPU" :span="1">
{{
cache.info?.used_cpu_user_children !== undefined
? parseFloat(cache.info.used_cpu_user_children).toFixed(2)
: "-"
}}
</el-descriptions-item>
<el-descriptions-item align="center" label="内存配置" :span="1">
{{ cache.info?.maxmemory_human || "-" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="AOF是否开启" :span="1">
{{ cache.info?.aof_enabled === "0" ? "否" : "是" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="RDB是否成功" :span="1">
{{ cache.info?.rdb_last_bgsave_status || "-" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="Key数量" :span="1">
{{ cache.dbSize || "-" }}
</el-descriptions-item>
<el-descriptions-item
align="center"
label="网络入口/出口"
:span="1"
>
{{
cache.info
? `${cache.info.instantaneous_input_kbps}kps/${cache.info.instantaneous_output_kbps}kps`
: "-"
}}
</el-descriptions-item>
<el-descriptions-item align="center" label="内存使用率" :span="1">
{{
cache.info?.used_memory !== undefined &&
cache.info?.maxmemory !== undefined &&
cache.info.maxmemory > 0
? (
(cache.info.used_memory / cache.info.maxmemory) *
100
).toFixed(2) + "%"
: "-"
}}
</el-descriptions-item>
<el-descriptions-item align="center" label="操作系统" :span="1">
{{ cache.info?.os || "-" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="架构位数" :span="1">
{{ cache.info?.arch_bits || "-" }}
</el-descriptions-item>
<el-descriptions-item align="center" label="多路复用API" :span="1">
{{ cache.info?.multiplexing_api || "-" }}
</el-descriptions-item>
</el-descriptions>
</el-card>
</re-col>
<re-col :value="12" :xs="24" :sm="24">
<el-card shadow="never">
<template #header>
<div class="flex items-center justify-center">
<IconifyIconOffline :icon="PieChart" />
<span class="header-title">命令统计</span>
</div>
</template>
<div ref="commandstats" style="height: 420px" />
</el-card>
</re-col>
<re-col :value="12" :xs="24" :sm="24">
<el-card shadow="never">
<template #header>
<div class="flex items-center justify-center">
<IconifyIconOffline :icon="Odometer" />
<span class="header-title">内存信息</span>
</div>
</template>
<div ref="usedmemory" style="height: 420px" />
</el-card>
</re-col>
</el-row>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: "MonitorCache"
});
import ReCol from "@/components/ReCol";
import { ref, onMounted, nextTick, reactive } from "vue";
import { getCachedMonitorInfoAPI } from "@/api/monitor";
import * as echarts from "echarts";
import Monitor from "@iconify-icons/ep/monitor";
import PieChart from "@iconify-icons/ep/pie-chart";
import Odometer from "@iconify-icons/ep/odometer";
const cache = reactive({
commandStats: [] as { name: string; value: number }[],
dbSize: 0,
info: {
redis_version: "",
redis_mode: "",
tcp_port: 0,
connected_clients: 0,
uptime_in_days: 0,
used_memory_human: "",
used_cpu_user_children: "",
maxmemory_human: "",
aof_enabled: "",
rdb_last_bgsave_status: "",
instantaneous_input_kbps: 0,
instantaneous_output_kbps: 0,
used_memory: 0,
maxmemory: 0,
os: "",
arch_bits: 0,
multiplexing_api: ""
}
});
const commandstats = ref(null);
const usedmemory = ref(null);
const getCacheInfo = async () => {
const res = await getCachedMonitorInfoAPI();
if (res.success) {
Object.assign(cache, res.data);
nextTick(() => {
initCommandStatsChart();
initUsedMemoryChart();
});
}
};
const initCommandStatsChart = () => {
const chart = echarts.init(commandstats.value);
const commandData = cache.commandStats || [];
const seriesData = commandData.map((cmd: any) => ({
name: cmd.name,
value: parseInt(cmd.value, 10)
}));
const option = {
title: {
text: "命令统计",
left: "center"
},
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b}: {c} ({d}%)"
},
legend: {
orient: "vertical",
left: "left"
},
series: [
{
name: "命令",
type: "pie",
radius: "50%",
data: seriesData,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
},
label: {
show: true,
formatter: "{b}: {c} ({d}%)"
}
}
]
};
chart.setOption(option);
};
const initUsedMemoryChart = () => {
const chart = echarts.init(usedmemory.value);
const usedMemory = cache.info?.used_memory || 0;
const maxMemory = cache.info?.maxmemory || 0;
const option = {
title: {
text: "内存信息",
left: "center"
},
tooltip: {
formatter: "{b} <br/>{a} : " + cache.info.used_memory_human
},
series: [
{
name: "峰值",
type: "gauge",
min: 0,
max: 1000,
detail: {
formatter: cache.info.used_memory_human
},
data: [
{
value: parseFloat(cache.info.used_memory_human),
name: "内存消耗"
}
]
}
]
};
chart.setOption(option);
};
onMounted(async () => {
await getCacheInfo();
setInterval(() => {
getCacheInfo();
}, 60000);
});
</script>
<style scoped lang="scss">
.icon {
width: 1em;
height: 1em;
vertical-align: middle;
}
.header-title {
margin-left: 0.5em;
vertical-align: middle;
}
.centered-descriptions {
:deep(.el-descriptions-item__container) {
display: flex;
align-items: center;
justify-content: center;
}
:deep(.el-descriptions-item__label) {
text-align: center;
}
:deep(.el-descriptions-item__content) {
text-align: center;
}
}
</style>