部分逻辑优化,涉及进程检测代码

This commit is contained in:
2026-08-07 22:35:08 +08:00
parent d7f3ffd476
commit 5f028dae4c
2 changed files with 92 additions and 19 deletions
+5 -5
View File
@@ -666,10 +666,10 @@ print_environment_summary() {
print_title print_title
print_subtitle "环境检测结果" print_subtitle "环境检测结果"
echo "" echo ""
echo -e " 操作系统:${NC} $OS $OS_VERSION" echo -e " 操作系统: $OS $OS_VERSION"
echo -e " CPU 架构:${NC} $ARCH" echo -e " CPU 架构: $ARCH"
echo "" echo ""
echo " 必要工具:${NC}" echo " 必要工具:"
if [ "$HAS_GIT" = true ]; then if [ "$HAS_GIT" = true ]; then
echo -e " git ✅ 已安装 ($(git --version | awk '{print $3}'))" echo -e " git ✅ 已安装 ($(git --version | awk '{print $3}'))"
else else
@@ -686,7 +686,7 @@ print_environment_summary() {
echo " wget ❌ 未安装 (将自动安装)" echo " wget ❌ 未安装 (将自动安装)"
fi fi
echo "" echo ""
echo " Docker 环境:${NC}" echo " Docker 环境:"
if [ "$HAS_DOCKER" = true ]; then if [ "$HAS_DOCKER" = true ]; then
echo -e " docker ✅ 已安装 ($(docker --version | awk '{print $3}' | tr -d ','))" echo -e " docker ✅ 已安装 ($(docker --version | awk '{print $3}' | tr -d ','))"
else else
@@ -696,7 +696,7 @@ print_environment_summary() {
fi fi
if [ "$CONTAINER_EXISTS" = true ]; then if [ "$CONTAINER_EXISTS" = true ]; then
echo "" echo ""
echo " 容器状态:${NC}" echo " 容器状态:"
if [ "$CONTAINER_RUNNING" = true ]; then if [ "$CONTAINER_RUNNING" = true ]; then
echo -e " frpc-console ✅ 运行中" echo -e " frpc-console ✅ 运行中"
else else
+87 -14
View File
@@ -17,6 +17,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@@ -31,11 +32,15 @@ import (
const ( const (
LockFileName = ".frpc.lock" LockFileName = ".frpc.lock"
PortCheckTimeout = 500 * time.Millisecond PortCheckTimeout = 500 * time.Millisecond
StartWaitTime = 500 * time.Millisecond
StopMaxWaitTime = 5 * time.Second StopMaxWaitTime = 5 * time.Second
LockAcquireTimeout = 30 * time.Second LockAcquireTimeout = 30 * time.Second
LockRetryInterval = 100 * time.Millisecond LockRetryInterval = 100 * time.Millisecond
APITimeout = 2 * time.Second APITimeout = 2 * time.Second
// 启动检测参数
StartupMaxAttempts = 25 // 最多检测 25 次
StartupRetryDelay = 200 * time.Millisecond // 每次间隔 200ms
// 总超时 = 25 * 200ms = 5 秒
) )
// ================================================================ // ================================================================
@@ -115,7 +120,7 @@ func (pm *ProcessManager) AdminPort() int {
} }
// ================================================================ // ================================================================
// 配置读取 (带调试日志) // 配置读取
// ================================================================ // ================================================================
func (pm *ProcessManager) LoadConfig() error { func (pm *ProcessManager) LoadConfig() error {
@@ -390,6 +395,32 @@ func (pm *ProcessManager) isFRPCProcess(pid int) (bool, string) {
return false, "" return false, ""
} }
// ================================================================
// 进程存活检测
// ================================================================
// isProcessAlive 检查 PID 对应的进程是否存活
func (pm *ProcessManager) isProcessAlive(pid int) bool {
if pid <= 0 {
return false
}
if runtime.GOOS == "windows" {
cmd := exec.Command("tasklist", "/FI", "PID eq", strconv.Itoa(pid))
output, err := cmd.CombinedOutput()
if err != nil {
return false
}
return strings.Contains(string(output), strconv.Itoa(pid))
}
process, err := os.FindProcess(pid)
if err != nil {
return false
}
return process.Signal(syscall.Signal(0)) == nil
}
// ================================================================ // ================================================================
// PID 文件操作 // PID 文件操作
// ================================================================ // ================================================================
@@ -525,19 +556,46 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return fmt.Errorf("启动 frpc 失败: %w", err) return fmt.Errorf("启动 frpc 失败: %w", err)
} }
time.Sleep(StartWaitTime)
occupied, err := pm.CheckPort() // ============================================================
if err != nil { // 组合状态检测:最多等待 5 秒,三条件全部满足才算成功
return fmt.Errorf("验证启动状态失败: %w", err) // ============================================================
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()
if err == nil && occupied {
// 三项全部满足!
pm.writePIDFile(pid)
log.Printf("[INFO] frpc 启动成功 (PID: %d, 端口: %d),耗时 %dms",
pid, pm.adminPort, attempt*int(StartupRetryDelay/time.Millisecond))
return nil
}
time.Sleep(StartupRetryDelay)
} }
if !occupied {
return fmt.Errorf("frpc 启动失败: 端口未监听") // 超时:输出诊断信息
} pid := pm.readPIDFile()
pid, _ := pm.getPIDByPort(pm.adminPort)
if pid > 0 { if pid > 0 {
pm.writePIDFile(pid) alive := pm.isProcessAlive(pid)
occupied, _ := pm.CheckPort()
return fmt.Errorf("frpc 启动超时: PID=%d, 进程存活=%v, 端口监听=%v", pid, alive, occupied)
} }
return nil
return fmt.Errorf("frpc 启动超时: 未生成 PID 文件")
} }
func (pm *ProcessManager) Stop(ctx context.Context) error { func (pm *ProcessManager) Stop(ctx context.Context) error {
@@ -548,7 +606,7 @@ func (pm *ProcessManager) Stop(ctx context.Context) error {
return pm.stopLocked(ctx) return pm.stopLocked(ctx)
} }
func (pm *ProcessManager) stopLocked(ctx context.Context) error { func (pm *ProcessManager) stopLocked(_ context.Context) error {
portStatus, err := pm.GetPortStatus() portStatus, err := pm.GetPortStatus()
if err != nil { if err != nil {
return fmt.Errorf("检测端口状态失败: %w", err) return fmt.Errorf("检测端口状态失败: %w", err)
@@ -569,6 +627,8 @@ func (pm *ProcessManager) stopLocked(ctx context.Context) error {
return fmt.Errorf("无法确定 frpc 进程 PID") return fmt.Errorf("无法确定 frpc 进程 PID")
} }
} }
// 发送 SIGTERM (优雅停止)
proc, err := os.FindProcess(pid) proc, err := os.FindProcess(pid)
if err != nil { if err != nil {
pm.deletePIDFile() pm.deletePIDFile()
@@ -578,6 +638,8 @@ func (pm *ProcessManager) stopLocked(ctx context.Context) error {
pm.deletePIDFile() pm.deletePIDFile()
return nil return nil
} }
// 等待端口释放
start := time.Now() start := time.Now()
for time.Since(start) < StopMaxWaitTime { for time.Since(start) < StopMaxWaitTime {
occupied, _ := pm.CheckPort() occupied, _ := pm.CheckPort()
@@ -587,11 +649,20 @@ func (pm *ProcessManager) stopLocked(ctx context.Context) error {
} }
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
} }
proc.Kill()
// 端口未释放, 强制 kill
if runtime.GOOS == "windows" {
cmd := exec.Command("taskkill", "/F", "/IM", "frpc.exe")
cmd.Run()
} else {
proc.Kill()
}
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
if occupied, _ := pm.CheckPort(); occupied { if occupied, _ := pm.CheckPort(); occupied {
return fmt.Errorf("强制停止失败: 端口仍被占用") return fmt.Errorf("强制停止失败: 端口仍被占用")
} }
pm.deletePIDFile() pm.deletePIDFile()
return nil return nil
} }
@@ -604,6 +675,8 @@ func (pm *ProcessManager) Restart(ctx context.Context) error {
if err := pm.stopLocked(ctx); err != nil { if err := pm.stopLocked(ctx); err != nil {
return fmt.Errorf("停止失败: %w", err) return fmt.Errorf("停止失败: %w", err)
} }
// 等待端口完全释放
time.Sleep(1 * time.Second)
if err := pm.startLocked(ctx); err != nil { if err := pm.startLocked(ctx); err != nil {
return fmt.Errorf("启动失败: %w", err) return fmt.Errorf("启动失败: %w", err)
} }