新的进程回收动作状态机

This commit is contained in:
2026-08-07 23:22:57 +08:00
parent 21d1a4d180
commit 94cd57a459
+44 -5
View File
@@ -486,6 +486,34 @@ func (pm *ProcessManager) killProcess(pid int) error {
return process.Kill() return process.Kill()
} }
// killAllFrpc 强制杀死所有 frpc 进程(包括孤儿)
func (pm *ProcessManager) killAllFrpc() {
log.Printf("[INFO] 清理所有 frpc 进程...")
if runtime.GOOS == "windows" {
exec.Command("taskkill", "/F", "/IM", "frpc.exe").Run()
} else {
exec.Command("pkill", "-9", "-f", "frpc").Run()
}
time.Sleep(1 * time.Second)
pm.deletePIDFile()
}
// countFrpcProcesses 统计当前 frpc 进程数量
func (pm *ProcessManager) countFrpcProcesses() int {
if runtime.GOOS == "windows" {
cmd := exec.Command("tasklist", "/FI", "IMAGENAME eq frpc.exe")
out, _ := cmd.CombinedOutput()
return strings.Count(string(out), "frpc.exe")
}
cmd := exec.Command("pgrep", "-c", "-f", "frpc")
out, err := cmd.Output()
if err != nil {
return 0
}
count, _ := strconv.Atoi(strings.TrimSpace(string(out)))
return count
}
func (pm *ProcessManager) cleanupOrphans() { func (pm *ProcessManager) cleanupOrphans() {
result := pm.CheckPort() result := pm.CheckPort()
if !result.Ready { if !result.Ready {
@@ -619,7 +647,6 @@ func (pm *ProcessManager) computeState(pid int, result PortCheckResult) ProcessS
stderr := pm.getLastError() stderr := pm.getLastError()
frpReady := pm.isFRPReady(stdout) frpReady := pm.isFRPReady(stdout)
// 获取退出码
pm.exitMu.Lock() pm.exitMu.Lock()
exitCode := pm.exitCode exitCode := pm.exitCode
pm.exitMu.Unlock() pm.exitMu.Unlock()
@@ -649,7 +676,6 @@ func (pm *ProcessManager) computeState(pid int, result PortCheckResult) ProcessS
return state return state
} }
// 异常退出
if exitCode != 0 && exitCode != -1 { if exitCode != 0 && exitCode != -1 {
state.Error = fmt.Sprintf("进程异常退出 (exit code: %d)", exitCode) state.Error = fmt.Sprintf("进程异常退出 (exit code: %d)", exitCode)
if stderr != "" { if stderr != "" {
@@ -677,6 +703,11 @@ func (pm *ProcessManager) computeState(pid int, result PortCheckResult) ProcessS
if portReady && frpReady { if portReady && frpReady {
state.Phase = PhaseRunning state.Phase = PhaseRunning
// 检测多进程
if count := pm.countFrpcProcesses(); count > 1 {
state.Phase = PhaseDegraded
state.Error = fmt.Sprintf("检测到 %d 个 frpc 进程,可能存在多开", count)
}
return state return state
} }
@@ -761,7 +792,12 @@ func (pm *ProcessManager) Start(ctx context.Context) error {
} }
defer pm.Unlock() defer pm.Unlock()
// 先杀所有 frpc 进程(避免多开)
pm.killAllFrpc()
// 清理孤儿
pm.cleanupOrphans() pm.cleanupOrphans()
return pm.startLocked(ctx) return pm.startLocked(ctx)
} }
@@ -779,7 +815,6 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
pm.startTime = time.Now() pm.startTime = time.Now()
pm.expectedStop = false pm.expectedStop = false
// 重置退出码
pm.exitMu.Lock() pm.exitMu.Lock()
pm.exitCode = -1 pm.exitCode = -1
pm.exitMu.Unlock() pm.exitMu.Unlock()
@@ -847,15 +882,18 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
time.Sleep(StartupRetryDelay) time.Sleep(StartupRetryDelay)
} }
// 超时:检查进程状态
if pm.isProcessAlive(cmd.Process.Pid) { if pm.isProcessAlive(cmd.Process.Pid) {
stderr := pm.getLastError() stderr := pm.getLastError()
log.Printf("[WARN] frpc 启动超时 (PID: %d),当前 stderr: %s", cmd.Process.Pid, stderr) log.Printf("[WARN] frpc 启动超时 (PID: %d),当前 stderr: %s", cmd.Process.Pid, stderr)
// 进程还在但端口没起来,尝试获取 frpc admin API 版本信息
if info := pm.getFRPCStatus(cmd.Process.Pid); info != nil && info.Version != "" { if info := pm.getFRPCStatus(cmd.Process.Pid); info != nil && info.Version != "" {
log.Printf("[INFO] frpc admin API 可访问,版本: %s", info.Version) log.Printf("[INFO] frpc admin API 可访问,版本: %s", info.Version)
// API 可访问说明 frpc 已正常运行,只是端口检测有误 pm.writePIDFile(cmd.Process.Pid)
return nil return nil
} }
// 启动失败:杀掉进程
pm.killProcess(cmd.Process.Pid)
pm.deletePIDFile()
return fmt.Errorf("frpc 启动超时: 进程存在但端口未就绪") return fmt.Errorf("frpc 启动超时: 进程存在但端口未就绪")
} }
@@ -957,6 +995,7 @@ func (pm *ProcessManager) HealthCheck() map[string]interface{} {
result["phase"] = state.Phase result["phase"] = state.Phase
result["pid"] = state.PID result["pid"] = state.PID
result["port"] = state.Port
result["alive"] = state.Alive result["alive"] = state.Alive
result["port_ready"] = state.PortReady result["port_ready"] = state.PortReady
result["frp_ready"] = state.FRPReady result["frp_ready"] = state.FRPReady