diff --git a/internal/controllers/monitor_controller.go b/internal/controllers/monitor_controller.go index baf5656..fcb5889 100644 --- a/internal/controllers/monitor_controller.go +++ b/internal/controllers/monitor_controller.go @@ -3,6 +3,7 @@ package controllers import ( "net/http" "runtime" + "sync" "time" "github.com/engigu/baihu-panel/internal/services/tasks" @@ -10,10 +11,22 @@ import ( "github.com/gin-gonic/gin" "github.com/gorilla/websocket" + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v3/host" + "github.com/shirou/gopsutil/v3/mem" ) type MonitorController struct { executorService *tasks.ExecutorService + + // 缓存物理机状态 + hostMu sync.RWMutex + lastUpdate time.Time + cpuPercent float64 + vMem *mem.VirtualMemoryStat + diskUsage *disk.UsageStat + hostInfo *host.InfoStat } func NewMonitorController(executorService *tasks.ExecutorService) *MonitorController { @@ -69,10 +82,50 @@ func (mc *MonitorController) sendMonitorData(ws *websocket.Conn) error { }) } +func (mc *MonitorController) updateHostMetrics() { + mc.hostMu.Lock() + defer mc.hostMu.Unlock() + + // 缓存 2 秒 + if time.Since(mc.lastUpdate) < 2*time.Second && mc.vMem != nil { + return + } + + cpuPercents, _ := cpu.Percent(0, false) + if len(cpuPercents) > 0 { + mc.cpuPercent = cpuPercents[0] + } + mc.vMem, _ = mem.VirtualMemory() + mc.diskUsage, _ = disk.Usage("/") + mc.hostInfo, _ = host.Info() + mc.lastUpdate = time.Now() +} + func (mc *MonitorController) getMonitorData() gin.H { var m runtime.MemStats runtime.ReadMemStats(&m) + // 更新并读取缓存的物理主机指标 + mc.updateHostMetrics() + + mc.hostMu.RLock() + cpuPercent := mc.cpuPercent + vMem := mc.vMem + diskUsage := mc.diskUsage + hostInfo := mc.hostInfo + mc.hostMu.RUnlock() + + // 提供默认值防空指针 + if vMem == nil { + vMem = &mem.VirtualMemoryStat{} + } + if diskUsage == nil { + diskUsage = &disk.UsageStat{} + } + if hostInfo == nil { + hostInfo = &host.InfoStat{} + } + return gin.H{ "env": gin.H{ "os": runtime.GOOS, @@ -81,6 +134,17 @@ func (mc *MonitorController) getMonitorData() gin.H { "num_cpu": runtime.NumCPU(), "goroutines": runtime.NumGoroutine(), }, + "host": gin.H{ + "cpu_percent": cpuPercent, + "mem_total": vMem.Total, + "mem_used": vMem.Used, + "mem_percent": vMem.UsedPercent, + "disk_total": diskUsage.Total, + "disk_used": diskUsage.Used, + "disk_percent": diskUsage.UsedPercent, + "uptime": hostInfo.Uptime, + "platform": hostInfo.Platform + " " + hostInfo.PlatformVersion, + }, "mem": gin.H{ "alloc": m.Alloc, "total_alloc": m.TotalAlloc, diff --git a/web/src/api/index.ts b/web/src/api/index.ts index d9533b0..8e773d4 100644 --- a/web/src/api/index.ts +++ b/web/src/api/index.ts @@ -17,6 +17,17 @@ export interface MonitorStats { num_cpu: number goroutines: number } + host: { + cpu_percent: number + mem_total: number + mem_used: number + mem_percent: number + disk_total: number + disk_used: number + disk_percent: number + uptime: number + platform: string + } mem: { alloc: number total_alloc: number diff --git a/web/src/views/monitor/Monitor.vue b/web/src/views/monitor/Monitor.vue index 3dafb55..20b3b14 100644 --- a/web/src/views/monitor/Monitor.vue +++ b/web/src/views/monitor/Monitor.vue @@ -4,7 +4,8 @@ import type { MonitorStats } from '@/api' import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' -import { RefreshCw } from 'lucide-vue-next' +import StatusDot from '@/components/StatusDot.vue' +import { RefreshCw, Zap, LayoutList, FileText, Server, Cpu, MemoryStick, HardDrive } from 'lucide-vue-next' import { Chart as ChartJS, @@ -298,7 +299,61 @@ const schedulerChartData = computed(() => ({ -
+ + + + +
+ + + CPU 使用率 + + +
+ + + + +
{{ stats.host.cpu_percent.toFixed(1) }}%
+
+
宿主机物理核心负载
+
+
+ + + + 内存使用率 + + +
+ + + + +
{{ stats.host.mem_percent.toFixed(1) }}%
+
+
{{ formatBytes(stats.host.mem_used) }} / {{ formatBytes(stats.host.mem_total) }}
+
+
+ + + + 磁盘使用率 + + +
+ + + + +
{{ stats.host.disk_percent.toFixed(1) }}%
+
+
{{ formatBytes(stats.host.disk_used) }} / {{ formatBytes(stats.host.disk_total) }}
+
+
+
+ +
执行环境 @@ -339,10 +394,12 @@ const schedulerChartData = computed(() => ({
Worker #{{ worker.id }} - 🟢 空闲 (Idle) + + 空闲 (Idle) - ⚡ 执行中 (Running) + + 执行中 (Running)
@@ -356,7 +413,9 @@ const schedulerChartData = computed(() => ({
+
+
内存概览 @@ -395,9 +454,9 @@ const schedulerChartData = computed(() => ({
-
下次 GC 目标 (NextGC)
{{ formatBytes(stats.gc.next_gc) }}
-
上次 GC 时间 (LastGC)
{{ stats.gc.last_gc ? new Date(stats.gc.last_gc / 1000000).toLocaleString() : '-' }}
-
GC 总停顿时间 (PauseTotalNs)
{{ formatNs(stats.gc.pause_total_ns) }}
+
下次 GC目标 (NextGC)
{{ formatBytes(stats.gc.next_gc) }}
+
上次 GC时间 (LastGC)
{{ stats.gc.last_gc ? new Date(stats.gc.last_gc / 1000000).toLocaleString() : '-' }}
+
GC停顿总时 (PauseTotal)
{{ formatNs(stats.gc.pause_total_ns) }}
执行次数 (NumGC)
{{ stats.gc.num_gc }}