From eda9d955fea43a3b7f11e396899e5ed403789e1b Mon Sep 17 00:00:00 2001 From: 1 <1@72wo.cn> Date: Fri, 23 Jan 2026 21:53:54 +0000 Subject: [PATCH] 1 --- modules/base/middleware/server.go | 121 ++++++++++++++---------------- 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/modules/base/middleware/server.go b/modules/base/middleware/server.go index a49a3f998..d00144d4d 100644 --- a/modules/base/middleware/server.go +++ b/modules/base/middleware/server.go @@ -198,12 +198,10 @@ else fi #!/bin/bash -# ===== 优雅终止logic会话(先等内部程序退出 → 再退screen)===== +# ===== 优雅终止logic会话(解决screen -ls卡住问题)===== echo "===== 优雅终止logic会话 =====" -SCREEN_PID="" # 替换为你实际的screen名称(示例:logic) -SCREEN_NAME="%s{screen_name}" -# 日志文件路径(可根据需要调整) +SCREEN_NAME="logic" LOG_FILE="./screen_logic_exit.log" # 调试开关(如需详细日志,取消set -x注释) @@ -211,20 +209,17 @@ set -o pipefail export PS4='[DEBUG] ${BASH_SOURCE}:${LINENO} - ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' # set -x -# ========== 核心函数:基于kill -0的等待进程退出函数 ========== -# 等待进程结束(带60秒超时,用kill -0检测存活,输出进度点) +# ========== 核心函数(保留你的原版) ========== wait_for_process_exit() { local pidKilled=$1 - local begin=$(date +%s) # 记录开始时间(秒) + local begin=$(date +%s) local end - local timeout=60 # 最大等待时间(秒) + local timeout=60 - # 循环检测进程是否存活 while kill -0 $pidKilled > /dev/null 2>&1; do - echo -n "." # 输出进度点,直观显示等待过程 + echo -n "." sleep 1; - # 检查是否超时 end=$(date +%s) if [ $((end - begin)) -gt $timeout ]; then echo -e "\n⚠️ 等待进程$pidKilled退出超时(已等${timeout}秒)" @@ -232,50 +227,57 @@ wait_for_process_exit() { fi done - # 最终状态提示 if ! kill -0 $pidKilled > /dev/null 2>&1; then echo -e "\n✅ 进程$pidKilled已退出" fi } -# 定义:检查PID是否存活的函数(复用kill -0逻辑) pid_is_alive() { local pid=$1 if [ -n "$pid" ] && kill -0 "$pid" > /dev/null 2>&1; then - return 0 # PID存活 + return 0 else - return 1 # PID不存在 + return 1 fi } -# 定义:获取screen会话下的所有子进程PID get_inner_procs() { local screen_pid=$1 - # 5秒超时,避免pstree卡住 local procs=$(timeout 5 pstree -p "$screen_pid" 2>/dev/null | grep -oE '\([0-9]+\)' | tr -d '()' | grep -v "$screen_pid" | sort -u) - # 兜底:pstree失败时用pgrep按screen名称查找 if [ -z "$procs" ]; then procs=$(pgrep -f "SCREEN -S $SCREEN_NAME" 2>/dev/null | grep -v "$screen_pid") fi echo "$procs" } -# 第一步:提取screen主PID并校验 -SCREEN_PID=$(screen -ls "$SCREEN_NAME" | grep -oE '[0-9]+\.'"$SCREEN_NAME" | head -1 | cut -d. -f1) -if [ -z "$SCREEN_PID" ]; then - echo "ℹ️ 未找到$SCREEN_NAME会话对应的PID,跳过终止流程" - # 去掉exit 1,直接跳过后续逻辑,不影响其他脚本执行 -else - # ========== 仅当找到PID时,才执行以下终止流程 ========== - echo "✅ 找到$SCREEN_NAME,主PID:$SCREEN_PID" +screen_send_cmd() { + local cmd="$1" + local screen_full_id="$2" + # ^M需手动生成:Ctrl+v+回车 + screen -S "$screen_full_id" -p 0 -X stuff "${cmd}^M" + sleep 1 +} - # 第二步:导出【退出前】的实时日志(终端+文件双输出) +# ========== 核心修复:给screen -ls加超时,避免卡住 ========== +echo "===== 检测screen会话(5秒超时) =====" +# 关键修改:给整个提取命令加5秒超时,超时则直接设为空 +SCREEN_FULL_ID=$(timeout 5 screen -ls 2>/dev/null | grep -E "[0-9]+\.$SCREEN_NAME" | grep -v "Dead\|Invalid" | head -1 | awk '{print $1}') + +# 无论是否超时/卡住,只要SCREEN_FULL_ID为空,就直接走后续 +if [ -z "$SCREEN_FULL_ID" ]; then + echo "ℹ️ 未找到$SCREEN_NAME会话(或screen -ls执行超时),直接执行后续脚本" +else + # 找到会话:执行终止逻辑 + SCREEN_PID=$(echo "$SCREEN_FULL_ID" | cut -d. -f1) + echo "✅ 找到$SCREEN_NAME,主PID:$SCREEN_PID | 完整ID:$SCREEN_FULL_ID" + + # 导出退出前日志 echo -e "\n===== 【退出前】$SCREEN_NAME 内程序实时log =====" - screen -S "$SCREEN_NAME" -p 0 -X hardcopy -h "$LOG_FILE" 2>/dev/null + screen -S "$SCREEN_FULL_ID" -p 0 -X hardcopy -h "$LOG_FILE" 2>/dev/null cat "$LOG_FILE" - # 第三步:给所有子进程发SIGTERM信号,并等待进程退出 - echo -e "\n===== 开始给所有子进程发优雅退出信号(SIGTERM) =====" + # 给子进程发SIGTERM并等待 + echo -e "\n===== 给子进程发优雅退出信号(SIGTERM) =====" INNER_PROCS=$(get_inner_procs "$SCREEN_PID") if [ -z "$INNER_PROCS" ]; then echo "ℹ 未检测到$SCREEN_NAME下的子进程" @@ -284,9 +286,9 @@ else for pid in $INNER_PROCS; do if pid_is_alive "$pid"; then echo -n "📌 终止进程$pid并等待退出:" - kill -15 "$pid" # 发送SIGTERM优雅退出信号 + kill -15 "$pid" if [ $? -eq 0 ]; then - wait_for_process_exit "$pid" # 调用等待函数 + wait_for_process_exit "$pid" else echo "❌ 进程$pid:发送SIGTERM失败" fi @@ -296,54 +298,47 @@ else done fi - # 验证子进程是否全部退出 + # 验证子进程退出状态 echo -e "\n===== 验证子进程退出状态 =====" REMAIN_PROCS=$(get_inner_procs "$SCREEN_PID") - INNER_PROC_EXIST=true if [ -z "$REMAIN_PROCS" ]; then - INNER_PROC_EXIST=false echo "✅ $SCREEN_NAME内部所有程序已退出" else echo "⚠️ 仍有残留进程:$REMAIN_PROCS" fi - # 退出screen会话 - if [ "$INNER_PROC_EXIST" = false ]; then - echo "内部程序已退出,开始退出$SCREEN_NAME会话..." - # 尝试正常退出screen会话 - if screen -S "$SCREEN_NAME" -X quit 2>/dev/null; then - echo "✅ $SCREEN_NAME会话已通过screen -X quit退出" - else - echo "ℹ️ screen -X quit执行失败,检查PID是否存活..." - # 仅当PID存活时,执行kill - if pid_is_alive "$SCREEN_PID"; then - echo "📌 PID $SCREEN_PID 仍存活,尝试kill终止..." - timeout 5 kill -15 "$SCREEN_PID" 2>/dev/null - if [ $? -eq 0 ]; then - echo "✅ 已给screen主进程$SCREEN_PID发送SIGTERM信号" - else - echo "⚠️ kill $SCREEN_PID 失败" - fi - else - echo "ℹ️ PID $SCREEN_PID 已不存在,跳过kill操作" - fi - fi - sleep 2 - else - echo "⚠️ 内部程序未完全退出,跳过退出screen会话" - fi + # 投递exit命令退出screen + echo -e "\n===== 优雅退出screen会话 =====" + echo "向$SCREEN_NAME投递exit命令..." + screen_send_cmd "exit" "$SCREEN_FULL_ID" + + # 等待并验证最终状态 + echo -n "等待screen会话自动退出" + begin=$(date +%s) + while timeout 1 screen -ls "$SCREEN_NAME" 2>/dev/null | grep -q -E "[0-9]+\.$SCREEN_NAME"; do + echo -n "." + sleep 1 + if [ $((date +%s - begin)) -gt 30 ]; then + echo -e "\n⚠️ 等待screen退出超时(30秒)" + break + fi + done - # 最终验证 echo -e "\n===== 最终验证 =====" - if screen -ls "$SCREEN_NAME" 2>/dev/null | grep -q -E "[0-9]+\.$SCREEN_NAME"; then + if timeout 1 screen -ls "$SCREEN_NAME" 2>/dev/null | grep -q -E "[0-9]+\.$SCREEN_NAME"; then echo "❌ $SCREEN_NAME会话最终仍未退出" else echo "✅ $SCREEN_NAME会话已完全退出" fi fi -# 后续脚本可以从这里继续执行,不受上述逻辑影响 +# ========== 后续脚本:必执行,永不卡住 ========== echo -e "\n===== 终止logic会话流程结束,继续执行后续脚本 =====" +# 示例后续逻辑 +# echo "执行后续任务:备份日志、启动新进程等..." + + + # ===== 准备下载目录 ===== echo "创建工作目录:%s{work_dir}"