部分逻辑优化,涉及进程检测代码
This commit is contained in:
@@ -666,10 +666,10 @@ print_environment_summary() {
|
||||
print_title
|
||||
print_subtitle "环境检测结果"
|
||||
echo ""
|
||||
echo -e " 操作系统:${NC} $OS $OS_VERSION"
|
||||
echo -e " CPU 架构:${NC} $ARCH"
|
||||
echo -e " 操作系统: $OS $OS_VERSION"
|
||||
echo -e " CPU 架构: $ARCH"
|
||||
echo ""
|
||||
echo " 必要工具:${NC}"
|
||||
echo " 必要工具:"
|
||||
if [ "$HAS_GIT" = true ]; then
|
||||
echo -e " git ✅ 已安装 ($(git --version | awk '{print $3}'))"
|
||||
else
|
||||
@@ -686,7 +686,7 @@ print_environment_summary() {
|
||||
echo " wget ❌ 未安装 (将自动安装)"
|
||||
fi
|
||||
echo ""
|
||||
echo " Docker 环境:${NC}"
|
||||
echo " Docker 环境:"
|
||||
if [ "$HAS_DOCKER" = true ]; then
|
||||
echo -e " docker ✅ 已安装 ($(docker --version | awk '{print $3}' | tr -d ','))"
|
||||
else
|
||||
@@ -696,7 +696,7 @@ print_environment_summary() {
|
||||
fi
|
||||
if [ "$CONTAINER_EXISTS" = true ]; then
|
||||
echo ""
|
||||
echo " 容器状态:${NC}"
|
||||
echo " 容器状态:"
|
||||
if [ "$CONTAINER_RUNNING" = true ]; then
|
||||
echo -e " frpc-console ✅ 运行中"
|
||||
else
|
||||
|
||||
+86
-13
@@ -17,6 +17,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -31,11 +32,15 @@ import (
|
||||
const (
|
||||
LockFileName = ".frpc.lock"
|
||||
PortCheckTimeout = 500 * time.Millisecond
|
||||
StartWaitTime = 500 * time.Millisecond
|
||||
StopMaxWaitTime = 5 * time.Second
|
||||
LockAcquireTimeout = 30 * time.Second
|
||||
LockRetryInterval = 100 * time.Millisecond
|
||||
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 {
|
||||
@@ -390,6 +395,32 @@ func (pm *ProcessManager) isFRPCProcess(pid int) (bool, string) {
|
||||
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 文件操作
|
||||
// ================================================================
|
||||
@@ -525,21 +556,48 @@ func (pm *ProcessManager) startLocked(ctx context.Context) error {
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("启动 frpc 失败: %w", err)
|
||||
}
|
||||
time.Sleep(StartWaitTime)
|
||||
|
||||
// ============================================================
|
||||
// 组合状态检测:最多等待 5 秒,三条件全部满足才算成功
|
||||
// ============================================================
|
||||
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 {
|
||||
return fmt.Errorf("验证启动状态失败: %w", err)
|
||||
}
|
||||
if !occupied {
|
||||
return fmt.Errorf("frpc 启动失败: 端口未监听")
|
||||
}
|
||||
pid, _ := pm.getPIDByPort(pm.adminPort)
|
||||
if pid > 0 {
|
||||
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)
|
||||
}
|
||||
|
||||
// 超时:输出诊断信息
|
||||
pid := pm.readPIDFile()
|
||||
if pid > 0 {
|
||||
alive := pm.isProcessAlive(pid)
|
||||
occupied, _ := pm.CheckPort()
|
||||
return fmt.Errorf("frpc 启动超时: PID=%d, 进程存活=%v, 端口监听=%v", pid, alive, occupied)
|
||||
}
|
||||
|
||||
return fmt.Errorf("frpc 启动超时: 未生成 PID 文件")
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) Stop(ctx context.Context) error {
|
||||
if err := pm.Lock(); err != nil {
|
||||
return fmt.Errorf("获取锁失败: %w", err)
|
||||
@@ -548,7 +606,7 @@ func (pm *ProcessManager) Stop(ctx context.Context) error {
|
||||
return pm.stopLocked(ctx)
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) stopLocked(ctx context.Context) error {
|
||||
func (pm *ProcessManager) stopLocked(_ context.Context) error {
|
||||
portStatus, err := pm.GetPortStatus()
|
||||
if err != nil {
|
||||
return fmt.Errorf("检测端口状态失败: %w", err)
|
||||
@@ -569,6 +627,8 @@ func (pm *ProcessManager) stopLocked(ctx context.Context) error {
|
||||
return fmt.Errorf("无法确定 frpc 进程 PID")
|
||||
}
|
||||
}
|
||||
|
||||
// 发送 SIGTERM (优雅停止)
|
||||
proc, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
pm.deletePIDFile()
|
||||
@@ -578,6 +638,8 @@ func (pm *ProcessManager) stopLocked(ctx context.Context) error {
|
||||
pm.deletePIDFile()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 等待端口释放
|
||||
start := time.Now()
|
||||
for time.Since(start) < StopMaxWaitTime {
|
||||
occupied, _ := pm.CheckPort()
|
||||
@@ -587,11 +649,20 @@ func (pm *ProcessManager) stopLocked(ctx context.Context) error {
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
|
||||
// 端口未释放, 强制 kill
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd := exec.Command("taskkill", "/F", "/IM", "frpc.exe")
|
||||
cmd.Run()
|
||||
} else {
|
||||
proc.Kill()
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
if occupied, _ := pm.CheckPort(); occupied {
|
||||
return fmt.Errorf("强制停止失败: 端口仍被占用")
|
||||
}
|
||||
|
||||
pm.deletePIDFile()
|
||||
return nil
|
||||
}
|
||||
@@ -604,6 +675,8 @@ func (pm *ProcessManager) Restart(ctx context.Context) error {
|
||||
if err := pm.stopLocked(ctx); err != nil {
|
||||
return fmt.Errorf("停止失败: %w", err)
|
||||
}
|
||||
// 等待端口完全释放
|
||||
time.Sleep(1 * time.Second)
|
||||
if err := pm.startLocked(ctx); err != nil {
|
||||
return fmt.Errorf("启动失败: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user