package app import ( "bytes" "context" "joybuywork/internal/consts" "encoding/json" "fmt" "net" "runtime" "runtime/pprof" "time" ) var Info ServiceInfo func SetUpCollector() { Info = ServiceInfo{ Name: consts.ServiceName, NodeId: fmt.Sprint(NodeId), GoVersion: runtime.Version(), StartAt: time.Now().Unix(), LastReportAt: time.Now().Unix(), Ip: GetLocalIP(), } } type PProfCollector struct { } // 初始化采集器 func NewPProfCollector() *PProfCollector { return &PProfCollector{} } // 采集运行时指标 func (c *PProfCollector) collectRuntimeMetrics() map[string]any { var memStats runtime.MemStats runtime.ReadMemStats(&memStats) return map[string]any{ "timestamp": time.Now().Unix(), "heap_alloc": memStats.HeapAlloc, // 当前堆内存分配(字节) "heap_sys": memStats.HeapSys, // 堆内存总申请量 "goroutines": runtime.NumGoroutine(), // Goroutine 数量 "gc_count": memStats.NumGC, // GC 次数 "gc_pause_ns": memStats.PauseTotalNs, // GC 总暂停时间(纳秒) } } // 采集 Heap Profile 并存储到 Redis func (c *PProfCollector) saveHeapProfile() { var buf bytes.Buffer if err := pprof.WriteHeapProfile(&buf); err != nil { Logger.Error(context.Background(), fmt.Sprintf("Failed to collect heap profile: %v", err)) return } // 存储为 Redis String(二进制数据) key := fmt.Sprintf("joybuywork:app:heap:%v:%s", NodeId, time.Now().Format("20060102_1504")) if err := Redis.Cache.Set(context.Background(), key, buf.Bytes(), 30*time.Minute).Err(); err != nil { Logger.Error(context.Background(), fmt.Sprintf("Failed to save heap profile to Redis: %v", err)) } else { Logger.Info(context.Background(), fmt.Sprintf("Heap profile saved: %s", key)) } } // 采集 CPU Profile(需谨慎,仅在必要时启用) func (c *PProfCollector) saveCPUProfile(duration time.Duration) { var buf bytes.Buffer if err := pprof.StartCPUProfile(&buf); err != nil { Logger.Error(context.Background(), fmt.Sprintf("Failed to start CPU profile: %v", err)) return } time.Sleep(duration) pprof.StopCPUProfile() key := fmt.Sprintf("joybuywork:app:cpu:%v:%s", NodeId, time.Now().Format("20060102_1504")) if err := Redis.Cache.Set(context.Background(), key, buf.Bytes(), 30*time.Minute).Err(); err != nil { Logger.Error(context.Background(), fmt.Sprintf("Failed to save CPU profile to Redis: %v", err)) } else { Logger.Info(context.Background(), fmt.Sprintf("CPU profile saved: %s", key)) } } // 存储运行时指标到 Redis(JSON 格式) func (c *PProfCollector) saveRuntimeMetrics() { ctx := context.Background() // 指标存储 metrics := c.collectRuntimeMetrics() jsonData, err := json.Marshal(metrics) if err != nil { Logger.Error(ctx, fmt.Sprintf("Failed to marshal metrics: %v", err)) return } key := fmt.Sprintf("joybuywork:app:metrics:%v:%s", NodeId, time.Now().Format("20060102")) if err := Redis.Cache.HSet(context.Background(), key, time.Now().Format("1504"), jsonData).Err(); err != nil { Logger.Error(context.Background(), fmt.Sprintf("Failed to save metrics to Redis: %v", err)) } else { Redis.Cache.Expire(ctx, key, 24*time.Hour) Logger.Info(context.Background(), fmt.Sprintf("Metrics saved: %s", key)) } } // 启动定时采集任务 func (c *PProfCollector) Start() { c.saveRuntimeMetrics() c.saveHeapProfile() c.saveCPUProfile(30 * time.Second) // 可选:采集 10 秒 CPU 数据 } type ServiceInfo struct { Name string `json:"name"` //服务名称 NodeId string `json:"node_id"` //节点id GoVersion string `json:"go_version"` //GO版本 StartAt int64 `json:"start_at"` //启动时间戳 LastReportAt int64 `json:"last_report_at"` //最后上报时间戳 Ip string `json:"ip"` } func HeartBeat() { ctx := context.Background() // 服务存储 Info.LastReportAt = time.Now().Unix() jsonInfo, err := json.Marshal(Info) if err != nil { Logger.Error(ctx, fmt.Sprintf("Failed to collect service info to json: %v\n", err)) return } infokey := fmt.Sprintf("service_info:%s", Info.Name) err = Redis.Cache.HSet(ctx, infokey, Info.NodeId, string(jsonInfo)).Err() if err != nil { Logger.Error(ctx, fmt.Sprintf("Failed to report service info to Redis: %v\n", err)) } Redis.Cache.Expire(ctx, infokey, 1*time.Hour) } func GetLocalIP() string { addrs, err := net.InterfaceAddrs() if err != nil { return "" } for _, addr := range addrs { ipNet, ok := addr.(*net.IPNet) if ok && !ipNet.IP.IsLoopback() { if ipNet.IP.To4() != nil { return ipNet.IP.String() } } } return "" }