feat: add xhttp transport, network monitoring and kernel auto-switch

This commit is contained in:
xboard
2026-04-18 01:39:49 +08:00
parent 65d97f3da1
commit 71c53e1071
5 changed files with 175 additions and 1 deletions
+78
View File
@@ -2,6 +2,8 @@ package monitor
import (
"runtime"
"strings"
"sync"
"time"
"github.com/cedar2025/xboard-node/internal/nlog"
@@ -9,6 +11,7 @@ import (
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/load"
"github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net"
)
var startTime = time.Now()
@@ -18,6 +21,9 @@ func init() {
// always returns 0% because it has no prior sample. This throwaway call
// seeds the baseline so subsequent Collect() calls return real values.
cpu.Percent(500*time.Millisecond, false)
// Seed the network baseline so the first Collect() can compute rates.
collectNetSpeed()
}
// Status holds system resource metrics
@@ -36,11 +42,81 @@ type Status struct {
DiskUsed uint64
Goroutines int
// Network speed (bytes/sec), -1 means unavailable (first sample).
NetInSpeed float64
NetOutSpeed float64
// GC metrics (process-wide)
NumGC uint32
LastPauseMS float64
}
// netBaseline tracks the previous network counters for rate calculation.
var (
netMu sync.Mutex
netPrevRecv uint64
netPrevSent uint64
netPrevTime time.Time
netHasBase bool
)
// skipInterface returns true for loopback and common virtual interfaces.
func skipInterface(name string) bool {
lower := strings.ToLower(name)
for _, prefix := range []string{"lo", "docker", "veth", "br-", "virbr", "vnet", "tun", "tap"} {
if strings.HasPrefix(lower, prefix) {
return true
}
}
return false
}
// collectNetSpeed calculates network in/out bytes per second since last call.
// Returns -1, -1 on first call or if counters decreased (reboot).
func collectNetSpeed() (inSpeed, outSpeed float64) {
counters, err := net.IOCounters(true) // per-interface
if err != nil {
nlog.Core().Debug("failed to get network counters", "error", err)
return -1, -1
}
var totalRecv, totalSent uint64
for _, c := range counters {
if skipInterface(c.Name) {
continue
}
totalRecv += c.BytesRecv
totalSent += c.BytesSent
}
now := time.Now()
netMu.Lock()
defer netMu.Unlock()
if !netHasBase {
netPrevRecv, netPrevSent, netPrevTime, netHasBase = totalRecv, totalSent, now, true
return -1, -1
}
elapsed := now.Sub(netPrevTime).Seconds()
if elapsed <= 0 {
return -1, -1
}
// Counter decreased → system reboot or interface reset; reset baseline.
if totalRecv < netPrevRecv || totalSent < netPrevSent {
netPrevRecv, netPrevSent, netPrevTime = totalRecv, totalSent, now
return -1, -1
}
inSpeed = float64(totalRecv-netPrevRecv) / elapsed
outSpeed = float64(totalSent-netPrevSent) / elapsed
netPrevRecv, netPrevSent, netPrevTime = totalRecv, totalSent, now
return inSpeed, outSpeed
}
// Collect gathers current system metrics
func Collect() Status {
var s Status
@@ -79,6 +155,8 @@ func Collect() Status {
s.DiskUsed = diskStat.Used
}
s.NetInSpeed, s.NetOutSpeed = collectNetSpeed()
// GC metrics
var ms runtime.MemStats
runtime.ReadMemStats(&ms)