进程检测能力调整

This commit is contained in:
2026-08-07 22:43:27 +08:00
parent 5f028dae4c
commit acf15fe932
+62 -33
View File
@@ -399,7 +399,6 @@ func (pm *ProcessManager) isFRPCProcess(pid int) (bool, string) {
// 进程存活检测 // 进程存活检测
// ================================================================ // ================================================================
// isProcessAlive 检查 PID 对应的进程是否存活
func (pm *ProcessManager) isProcessAlive(pid int) bool { func (pm *ProcessManager) isProcessAlive(pid int) bool {
if pid <= 0 { if pid <= 0 {
return false return false
@@ -421,6 +420,50 @@ func (pm *ProcessManager) isProcessAlive(pid int) bool {
return process.Signal(syscall.Signal(0)) == nil return process.Signal(syscall.Signal(0)) == nil
} }
// ================================================================
// 进程清理
// ================================================================
func (pm *ProcessManager) killProcess(pid int) error {
if pid <= 0 {
return nil
}
if runtime.GOOS == "windows" {
cmd := exec.Command("taskkill", "/F", "/PID", strconv.Itoa(pid))
return cmd.Run()
}
process, err := os.FindProcess(pid)
if err != nil {
return err
}
return process.Kill()
}
func (pm *ProcessManager) cleanupOrphans() {
// 检查端口是否被占用
occupied, err := pm.CheckPort()
if err != nil || !occupied {
return
}
// 检查是否有 PID 文件
pid := pm.readPIDFile()
if pid > 0 && pm.isProcessAlive(pid) {
return
}
log.Printf("[WARN] 检测到孤儿 frpc 进程 (端口 %d 被占用但无有效 PID),正在清理...", pm.adminPort)
if runtime.GOOS == "windows" {
exec.Command("taskkill", "/F", "/IM", "frpc.exe").Run()
} else {
exec.Command("pkill", "-f", "frpc").Run()
}
pm.deletePIDFile()
time.Sleep(1 * time.Second)
}
// ================================================================ // ================================================================
// PID 文件操作 // PID 文件操作
// ================================================================ // ================================================================
@@ -532,6 +575,10 @@ func (pm *ProcessManager) Start(ctx context.Context) error {
return fmt.Errorf("获取锁失败: %w", err) return fmt.Errorf("获取锁失败: %w", err)
} }
defer pm.Unlock() defer pm.Unlock()
// 清理孤儿进程
pm.cleanupOrphans()
return pm.startLocked(ctx) return pm.startLocked(ctx)
} }
@@ -557,45 +604,28 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
return fmt.Errorf("启动 frpc 失败: %w", err) return fmt.Errorf("启动 frpc 失败: %w", err)
} }
// ============================================================ // 立即写入 PID 文件
// 组合状态检测:最多等待 5 秒,三条件全部满足才算成功 if err := pm.writePIDFile(cmd.Process.Pid); err != nil {
// ============================================================ log.Printf("[WARN] 写入 PID 文件失败: %v", err)
}
log.Printf("[DEBUG] frpc 进程已启动,PID: %d", cmd.Process.Pid)
// 检测端口是否监听(最多 5 秒)
for attempt := 0; attempt < StartupMaxAttempts; attempt++ { for attempt := 0; attempt < StartupMaxAttempts; attempt++ {
// 条件1: 检查 PID 文件
pid := pm.readPIDFile()
if pid <= 0 {
time.Sleep(StartupRetryDelay)
continue
}
// 条件2: 检查进程是否存活
if !pm.isProcessAlive(pid) {
time.Sleep(StartupRetryDelay)
continue
}
// 条件3: 检查端口是否可连接
occupied, err := pm.CheckPort() occupied, err := pm.CheckPort()
if err == nil && occupied { if err == nil && occupied {
// 三项全部满足!
pm.writePIDFile(pid)
log.Printf("[INFO] frpc 启动成功 (PID: %d, 端口: %d),耗时 %dms", log.Printf("[INFO] frpc 启动成功 (PID: %d, 端口: %d),耗时 %dms",
pid, pm.adminPort, attempt*int(StartupRetryDelay/time.Millisecond)) cmd.Process.Pid, pm.adminPort, attempt*int(StartupRetryDelay/time.Millisecond))
return nil return nil
} }
time.Sleep(StartupRetryDelay) time.Sleep(StartupRetryDelay)
} }
// 超时:输出诊断信息 // 端口监听超时:杀掉启动的进程并清理
pid := pm.readPIDFile() log.Printf("[WARN] frpc 启动超时 (PID: %d),正在清理...", cmd.Process.Pid)
if pid > 0 { pm.killProcess(cmd.Process.Pid)
alive := pm.isProcessAlive(pid) pm.deletePIDFile()
occupied, _ := pm.CheckPort() return fmt.Errorf("frpc 启动超时: 端口未监听 (PID: %d)", cmd.Process.Pid)
return fmt.Errorf("frpc 启动超时: PID=%d, 进程存活=%v, 端口监听=%v", pid, alive, occupied)
}
return fmt.Errorf("frpc 启动超时: 未生成 PID 文件")
} }
func (pm *ProcessManager) Stop(ctx context.Context) error { func (pm *ProcessManager) Stop(ctx context.Context) error {
@@ -652,8 +682,7 @@ func (pm *ProcessManager) stopLocked(_ context.Context) error {
// 端口未释放, 强制 kill // 端口未释放, 强制 kill
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
cmd := exec.Command("taskkill", "/F", "/IM", "frpc.exe") exec.Command("taskkill", "/F", "/IM", "frpc.exe").Run()
cmd.Run()
} else { } else {
proc.Kill() proc.Kill()
} }