128 lines
3.1 KiB
Go
128 lines
3.1 KiB
Go
package main
|
||
|
||
import (
|
||
"os/exec"
|
||
"strconv"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/shirou/gopsutil/v4/cpu"
|
||
"github.com/shirou/gopsutil/v4/mem"
|
||
gnet "github.com/shirou/gopsutil/v4/net"
|
||
"view/platform"
|
||
)
|
||
|
||
// HostMetrics 本机总览快照(端口监控页轮询)。
|
||
type HostMetrics struct {
|
||
At string `json:"at"`
|
||
CPUPercent float64 `json:"cpu"`
|
||
MemUsedMB float64 `json:"memUsedMB"`
|
||
MemTotalMB float64 `json:"memTotalMB"`
|
||
MemPercent float64 `json:"memPercent"`
|
||
GPUPercent float64 `json:"gpu"` // -1 表示不可用
|
||
GPUMemUsedMB float64 `json:"gpuMemUsedMB"`
|
||
GPUMemTotalMB float64 `json:"gpuMemTotalMB"`
|
||
GPUName string `json:"gpuName"`
|
||
NetRecvKBs float64 `json:"netRecvKBs"`
|
||
NetSentKBs float64 `json:"netSentKBs"`
|
||
}
|
||
|
||
var (
|
||
hostNetMu sync.Mutex
|
||
hostNetAt time.Time
|
||
hostNetRecv uint64
|
||
hostNetSent uint64
|
||
hostNetInit bool
|
||
)
|
||
|
||
// GetHostMetrics 采集 CPU / 内存 / 网卡吞吐;GPU 在有 nvidia-smi 时附带利用率。
|
||
func (a *App) GetHostMetrics() (HostMetrics, error) {
|
||
out := HostMetrics{At: time.Now().Format(time.RFC3339), GPUPercent: -1}
|
||
|
||
if percents, err := cpu.Percent(0, false); err == nil && len(percents) > 0 {
|
||
out.CPUPercent = percents[0]
|
||
if out.CPUPercent < 0 {
|
||
out.CPUPercent = 0
|
||
}
|
||
}
|
||
|
||
if vm, err := mem.VirtualMemory(); err == nil && vm != nil {
|
||
out.MemUsedMB = float64(vm.Used) / 1024 / 1024
|
||
out.MemTotalMB = float64(vm.Total) / 1024 / 1024
|
||
out.MemPercent = vm.UsedPercent
|
||
}
|
||
|
||
recvKB, sentKB := sampleHostNet()
|
||
out.NetRecvKBs = recvKB
|
||
out.NetSentKBs = sentKB
|
||
|
||
fillGPU(&out)
|
||
return out, nil
|
||
}
|
||
|
||
func sampleHostNet() (recvKBs, sentKBs float64) {
|
||
counters, err := gnet.IOCounters(false)
|
||
if err != nil || len(counters) == 0 {
|
||
return 0, 0
|
||
}
|
||
c := counters[0]
|
||
now := time.Now()
|
||
|
||
hostNetMu.Lock()
|
||
defer hostNetMu.Unlock()
|
||
if !hostNetInit {
|
||
hostNetInit = true
|
||
hostNetAt = now
|
||
hostNetRecv = c.BytesRecv
|
||
hostNetSent = c.BytesSent
|
||
return 0, 0
|
||
}
|
||
wall := now.Sub(hostNetAt).Seconds()
|
||
if wall <= 0 {
|
||
return 0, 0
|
||
}
|
||
recvKBs = float64(c.BytesRecv-hostNetRecv) / 1024 / wall
|
||
sentKBs = float64(c.BytesSent-hostNetSent) / 1024 / wall
|
||
if recvKBs < 0 {
|
||
recvKBs = 0
|
||
}
|
||
if sentKBs < 0 {
|
||
sentKBs = 0
|
||
}
|
||
hostNetAt = now
|
||
hostNetRecv = c.BytesRecv
|
||
hostNetSent = c.BytesSent
|
||
return recvKBs, sentKBs
|
||
}
|
||
|
||
// fillGPU 优先 nvidia-smi;失败则保持 gpu=-1。无窗口执行,避免端口监控轮询闪 cmd。
|
||
func fillGPU(out *HostMetrics) {
|
||
cmd := exec.Command("nvidia-smi",
|
||
"--query-gpu=name,utilization.gpu,memory.used,memory.total",
|
||
"--format=csv,noheader,nounits")
|
||
platform.ConfigureHidden(cmd)
|
||
raw, err := cmd.Output()
|
||
if err != nil {
|
||
return
|
||
}
|
||
line := strings.TrimSpace(strings.Split(string(raw), "\n")[0])
|
||
if line == "" {
|
||
return
|
||
}
|
||
parts := strings.Split(line, ",")
|
||
if len(parts) < 4 {
|
||
return
|
||
}
|
||
out.GPUName = strings.TrimSpace(parts[0])
|
||
if v, e := strconv.ParseFloat(strings.TrimSpace(parts[1]), 64); e == nil {
|
||
out.GPUPercent = v
|
||
}
|
||
if v, e := strconv.ParseFloat(strings.TrimSpace(parts[2]), 64); e == nil {
|
||
out.GPUMemUsedMB = v
|
||
}
|
||
if v, e := strconv.ParseFloat(strings.TrimSpace(parts[3]), 64); e == nil {
|
||
out.GPUMemTotalMB = v
|
||
}
|
||
}
|