package main import ( "fmt" "log" "os" "runtime" "strconv" "strings" "time" _ "github.com/gogf/gf/contrib/nosql/redis/v2" "github.com/gogf/gf/v2/os/gcmd" "github.com/gogf/gf/v2/os/gproc" "blazing/logic/service/player" "blazing/cool" "github.com/shirou/gopsutil/v4/mem" "blazing/modules/base/service" blservice "blazing/modules/player/service" "net/http" _ "net/http/pprof" ) // PprofWeb 启动pprof性能分析web服务 // PprofWeb 启动pprof web服务,仅重试2个端口保证监听成功 func PprofWeb() { // 开启pprof跟踪 runtime.SetMutexProfileFraction(1) runtime.SetBlockProfileRate(1) // 定义2个重试端口:主端口9909 + 备用端口9910 ports := []int{9909, 9910} // 遍历端口尝试监听 for _, port := range ports { addr := ":" + strconv.Itoa(port) fmt.Printf("[INFO] 尝试启动pprof服务,监听端口: %d\n", port) // 尝试监听并处理错误 err := http.ListenAndServe(addr, nil) // 只有端口被占用等致命错误才重试下一个端口 if err != nil { fmt.Printf("[WARN] 端口%d监听失败: %v\n", port, err) continue } // 监听成功则直接返回 return } // 所有端口都失败时的兜底 errMsg := "[FATAL] 端口9909/9910均监听失败,pprof服务启动失败" fmt.Println(errMsg) // 可选:根据业务需求决定是否panic // panic(errMsg) } func cleanup() { fmt.Println("执行优雅清理资源...") player.Mainplayer.Range(func(key uint32, value *player.ClientData) bool { value.Player.Kick(true) return false }) //fight.Fightpool.ReleaseTimeout(0) fmt.Println("资源清理完成,程序即将退出") } // signalHandlerForMain 主进程信号处理函数 func signalHandlerForMain(sig os.Signal) { cleanup() fmt.Println("MainProcess is shutting down due to signal:", sig.String()) } // main 程序主入口函数 func main() { // item := model.NeweggConfig() // item.GeneratedPetIDs = []model.GeneratedPetID{ // {PetID: 1, Prob: 0.01}, // {PetID: 2, Prob: 0.01}, // {PetID: 3, Prob: 0.01}, // {PetID: 4, Prob: 0.01}, // {PetID: 5, Prob: 0.01}, // {PetID: 6, Prob: 0.01}, // } // item.MalePet = 1 // item.FemalePet = 2 // _, err := g.DB(item.GroupName()).Model(item.TableName()).FieldsEx("id").Data(item).Insert() // if err != nil { // panic(err) // } //loadAccounts() // if cool.IsRedisMode { // go cool.ListenFunc(gctx.New()) // } // 解析命令行参数 cool.Config.GameOnlineID = gcmd.GetOpt("id", "1").Uint32() go Start() //注入service // if cool.Config.GameOnlineID == 2 { //只分析1服务器的 // go PprofWeb() // } //go PprofWeb() go monitorMemAndQuit() fmt.Println("Process start, pid:", os.Getpid()) gproc.AddSigHandlerShutdown( signalHandlerForMain, ) gproc.Listen() } const ( memThresholdRatio = 0.9 // 内存占用阈值70% checkInterval = 10 * time.Second // 内存检测间隔,可按需调整 ) // 监控内存,超阈值则优雅退出程序 func monitorMemAndQuit() { var memStats runtime.MemStats for { // 1. 获取系统总内存和可用内存 sysMem, err := mem.VirtualMemory() if err != nil { log.Printf("获取系统内存失败:%v\n", err) time.Sleep(checkInterval) continue } // 2. 获取Go进程当前堆内存占用(进程实际使用的核心内存) runtime.ReadMemStats(&memStats) procUsedMem := memStats.HeapInuse // 进程堆内存占用(字节) sysTotalMem := sysMem.Total // 系统总内存(字节) // 3. 计算进程内存占系统总内存的比例 usedRatio := float64(procUsedMem) / float64(sysTotalMem) // 格式化输出(MB),方便查看 procUsedMB := procUsedMem / 1024 / 1024 sysTotalMB := sysTotalMem / 1024 / 1024 log.Printf("当前内存:进程占用%vMB / 系统总%vMB,占比%.1f%%", procUsedMB, sysTotalMB, usedRatio*100) // 4. 超70%阈值,执行优雅退出 if usedRatio >= memThresholdRatio { log.Fatalf("内存占比达%.1f%%,超过90%%阈值,程序开始退出", usedRatio*100) // ########## 可选:这里添加你的优雅清理逻辑 ########## // 如:关闭数据库连接、释放文件句柄、保存业务状态、推送退出告警等 cleanup() // 退出程序,返回非0码(方便进程管理工具识别并重启) os.Exit(1) } time.Sleep(checkInterval) } } // loadAccounts 从CSV文件加载账号信息 func loadAccounts() { t1, _ := os.Getwd() data, err := os.ReadFile(t1 + "/b.csv") if err != nil { panic(err) } lines := strings.Split(string(data), "\n") for _, line := range lines { line = strings.TrimSpace(line) if line == "" { continue } t := service.NewBaseSysUserService().GetEamil(line) blservice.NewUserService(uint32(t.ID)).Info.Reg(t.Username, 0) } //fmt.Printf("加载 %d 个账号\n", len(accounts)) }