新的进程回收动作状态机
This commit is contained in:
@@ -486,6 +486,34 @@ func (pm *ProcessManager) killProcess(pid int) error {
|
||||
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() {
|
||||
result := pm.CheckPort()
|
||||
if !result.Ready {
|
||||
@@ -619,7 +647,6 @@ func (pm *ProcessManager) computeState(pid int, result PortCheckResult) ProcessS
|
||||
stderr := pm.getLastError()
|
||||
frpReady := pm.isFRPReady(stdout)
|
||||
|
||||
// 获取退出码
|
||||
pm.exitMu.Lock()
|
||||
exitCode := pm.exitCode
|
||||
pm.exitMu.Unlock()
|
||||
@@ -649,7 +676,6 @@ func (pm *ProcessManager) computeState(pid int, result PortCheckResult) ProcessS
|
||||
return state
|
||||
}
|
||||
|
||||
// 异常退出
|
||||
if exitCode != 0 && exitCode != -1 {
|
||||
state.Error = fmt.Sprintf("进程异常退出 (exit code: %d)", exitCode)
|
||||
if stderr != "" {
|
||||
@@ -677,6 +703,11 @@ func (pm *ProcessManager) computeState(pid int, result PortCheckResult) ProcessS
|
||||
|
||||
if portReady && frpReady {
|
||||
state.Phase = PhaseRunning
|
||||
// 检测多进程
|
||||
if count := pm.countFrpcProcesses(); count > 1 {
|
||||
state.Phase = PhaseDegraded
|
||||
state.Error = fmt.Sprintf("检测到 %d 个 frpc 进程,可能存在多开", count)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -761,7 +792,12 @@ func (pm *ProcessManager) Start(ctx context.Context) error {
|
||||
}
|
||||
defer pm.Unlock()
|
||||
|
||||
// 先杀所有 frpc 进程(避免多开)
|
||||
pm.killAllFrpc()
|
||||
|
||||
// 清理孤儿
|
||||
pm.cleanupOrphans()
|
||||
|
||||
return pm.startLocked(ctx)
|
||||
}
|
||||
|
||||
@@ -779,7 +815,6 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
|
||||
pm.startTime = time.Now()
|
||||
pm.expectedStop = false
|
||||
|
||||
// 重置退出码
|
||||
pm.exitMu.Lock()
|
||||
pm.exitCode = -1
|
||||
pm.exitMu.Unlock()
|
||||
@@ -847,15 +882,18 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
|
||||
time.Sleep(StartupRetryDelay)
|
||||
}
|
||||
|
||||
// 超时:检查进程状态
|
||||
if pm.isProcessAlive(cmd.Process.Pid) {
|
||||
stderr := pm.getLastError()
|
||||
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 != "" {
|
||||
log.Printf("[INFO] frpc admin API 可访问,版本: %s", info.Version)
|
||||
// API 可访问说明 frpc 已正常运行,只是端口检测有误
|
||||
pm.writePIDFile(cmd.Process.Pid)
|
||||
return nil
|
||||
}
|
||||
// 启动失败:杀掉进程
|
||||
pm.killProcess(cmd.Process.Pid)
|
||||
pm.deletePIDFile()
|
||||
return fmt.Errorf("frpc 启动超时: 进程存在但端口未就绪")
|
||||
}
|
||||
|
||||
@@ -957,6 +995,7 @@ func (pm *ProcessManager) HealthCheck() map[string]interface{} {
|
||||
|
||||
result["phase"] = state.Phase
|
||||
result["pid"] = state.PID
|
||||
result["port"] = state.Port
|
||||
result["alive"] = state.Alive
|
||||
result["port_ready"] = state.PortReady
|
||||
result["frp_ready"] = state.FRPReady
|
||||
|
||||
Reference in New Issue
Block a user