暂停运营
星梦云平台
自营平台已暂停运营,目前正在和第三方服务商探讨技术合作,敬请期待。

直观的图表展示服务器运行状态,帮助您快速掌握整体运行状况。
package main
import (
"context"
"fmt"
"log/slog"
"os"
"os/signal"
"strconv"
"strings"
"sync"
"syscall"
"time"
"github.com/joho/godotenv"
)
const (
trafficUpdateInterval = 30 * time.Second
saveTrafficInterval = 5 * time.Minute
reportInterval = 5 * time.Minute
httpTimeout = 30 * time.Second
)
type Config struct {
MasterAddress string
NodeName string
NodeKey string
TLSSkipVerify bool
}
func loadConfig() (Config, error) {
if err := godotenv.Load(); err != nil {
slog.Warn("加载 .env 文件失败(将使用系统环境变量)", "error", err)
}
master := os.Getenv("MASTER_ADDRESS")
nodeName := os.Getenv("NODE_NAME")
nodeKey := os.Getenv("NODE_KEY")
if master == "" || nodeName == "" || nodeKey == "" {
return Config{}, fmt.Errorf("配置不完整,请检查 .env 文件")
}
// TLS_SKIP_VERIFY 默认 false(校验证书);值为 true 时跳过校验
tlsSkipVerify := false
if v, err := strconv.ParseBool(os.Getenv("TLS_SKIP_VERIFY")); err == nil {
tlsSkipVerify = v
}
return Config{
MasterAddress: strings.TrimRight(strings.TrimSpace(master), "/"),
NodeName: nodeName,
NodeKey: nodeKey,
TLSSkipVerify: tlsSkipVerify,
}, nil
}
func saveTrafficOnExit(tm *TrafficMonitor) {
month, upload, download := tm.GetSnapshot()
if err := saveMonthlyTraffic(month, upload, download); err != nil {
slog.Error("退出前保存流量数据失败", "error", err)
} else {
slog.Info("流量数据已保存", "upload_mb", fmt.Sprintf("%.2f", upload), "download_mb", fmt.Sprintf("%.2f", download))
}
}
func main() {
slog.Info("探针程序启动")
config, err := loadConfig()
if err != nil {
slog.Error("加载配置失败", "error", err)
os.Exit(1)
}
initHTTPClient(config.TLSSkipVerify)
trafficMonitor := NewTrafficMonitor()
if month, upload, download, err := loadMonthlyTraffic(); err == nil {
trafficMonitor.SetMonthlyTraffic(month, upload, download)
slog.Info("已加载月流量数据",
"upload_mb", fmt.Sprintf("%.2f", upload),
"download_mb", fmt.Sprintf("%.2f", download))
} else {
slog.Warn("加载月流量数据失败,将从 0 开始累计", "error", err)
}
slog.Info("监控程序运行中", "node", config.NodeName, "master", config.MasterAddress)
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
var wg sync.WaitGroup
// 启动时立即建立基线:
// 1) 先采一次流量计数器作为基线,使下一个采样窗口即可算出真实带宽(#10)
// 2) 同步采一次系统指标填充缓存,保证即将开始的首轮上报有数据可用(#1)
if err := trafficMonitor.UpdateTraffic(); err != nil {
slog.Warn("建立流量基线失败,将在下个周期重试", "error", err)
}
if err := sampleSystemUsage(); err != nil {
slog.Warn("首次系统指标采样失败,将在下个周期重试", "error", err)
}
wg.Add(1)
go func() {
defer wg.Done()
ticker := time.NewTicker(trafficUpdateInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if err := trafficMonitor.UpdateTraffic(); err != nil {
slog.Error("更新流量失败", "error", err)
}
}
}
}()
// 系统指标(CPU/内存/磁盘)采样协程:与流量采样同周期在后台运行,
// 把 cpu.Percent 的 1s 阻塞从上报路径剥离到此处(#1)。
wg.Add(1)
go func() {
defer wg.Done()
ticker := time.NewTicker(trafficUpdateInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if err := sampleSystemUsage(); err != nil {
slog.Error("系统指标采样失败", "error", err)
}
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
ticker := time.NewTicker(saveTrafficInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
month, upload, download := trafficMonitor.GetSnapshot()
if err := saveMonthlyTraffic(month, upload, download); err != nil {
slog.Error("保存流量失败", "error", err)
}
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
// 先立即上报一次,再进入周期循环
if err := submitData(ctx, config, trafficMonitor); err != nil {
slog.Error("上报失败", "error", err)
}
ticker := time.NewTicker(reportInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if err := submitData(ctx, config, trafficMonitor); err != nil {
slog.Error("上报失败", "error", err)
}
}
}
}()
<-ctx.Done()
slog.Info("收到退出信号,正在等待任务退出...")
wg.Wait()
saveTrafficOnExit(trafficMonitor)
slog.Info("探针程序已退出")
}
自营平台已暂停运营,目前正在和第三方服务商探讨技术合作,敬请期待。