收敛后端重启脚本停机范围
This commit is contained in:
BIN
assets/员工账户.xlsx
Normal file
BIN
assets/员工账户.xlsx
Normal file
Binary file not shown.
@@ -11,8 +11,8 @@ TARGET_DIR="$ROOT_DIR/ruoyi-admin/target"
|
||||
JAR_NAME="ruoyi-admin.jar"
|
||||
SERVER_PORT=62318
|
||||
STOP_WAIT_SECONDS=30
|
||||
APP_KEYWORD="$JAR_NAME"
|
||||
JAVA_OPTS="-Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError"
|
||||
APP_MARKER="-Dccdi.backend.root=$ROOT_DIR"
|
||||
JAVA_OPTS="$APP_MARKER -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError"
|
||||
|
||||
timestamp() {
|
||||
date "+%Y-%m-%d %H:%M:%S"
|
||||
@@ -42,24 +42,54 @@ ensure_command() {
|
||||
fi
|
||||
}
|
||||
|
||||
is_managed_backend_pid() {
|
||||
pid="$1"
|
||||
if [ -z "${pid:-}" ] || ! kill -0 "$pid" 2>/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
args=$(ps -o args= -p "$pid" 2>/dev/null || true)
|
||||
if [ -z "${args:-}" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$args" in
|
||||
*"$APP_MARKER"*"$JAR_NAME"*|*"$JAR_NAME"*"$APP_MARKER"*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
file_pid=$(cat "$PID_FILE" 2>/dev/null || true)
|
||||
if [ "${file_pid:-}" = "$pid" ]; then
|
||||
case "$args" in
|
||||
*"java"*"-jar"*"$JAR_NAME"*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
collect_pids() {
|
||||
all_pids=""
|
||||
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
file_pid=$(cat "$PID_FILE" 2>/dev/null || true)
|
||||
if [ -n "${file_pid:-}" ] && kill -0 "$file_pid" 2>/dev/null; then
|
||||
if [ -n "${file_pid:-}" ] && is_managed_backend_pid "$file_pid"; then
|
||||
all_pids="$all_pids $file_pid"
|
||||
fi
|
||||
fi
|
||||
|
||||
port_pids=$(lsof -tiTCP:"$SERVER_PORT" -sTCP:LISTEN 2>/dev/null || true)
|
||||
if [ -n "${port_pids:-}" ]; then
|
||||
all_pids="$all_pids $port_pids"
|
||||
fi
|
||||
|
||||
app_pids=$(pgrep -f "$APP_KEYWORD" 2>/dev/null || true)
|
||||
if [ -n "${app_pids:-}" ]; then
|
||||
all_pids="$all_pids $app_pids"
|
||||
marker_pids=$(pgrep -f "$APP_MARKER" 2>/dev/null || true)
|
||||
if [ -n "${marker_pids:-}" ]; then
|
||||
for pid in $marker_pids; do
|
||||
if is_managed_backend_pid "$pid"; then
|
||||
all_pids="$all_pids $pid"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
unique_pids=""
|
||||
@@ -155,6 +185,12 @@ status_backend() {
|
||||
pids=$(collect_pids)
|
||||
if [ -n "${pids:-}" ]; then
|
||||
log_info "后端正在运行,进程: $pids"
|
||||
return 0
|
||||
fi
|
||||
|
||||
port_pids=$(lsof -tiTCP:"$SERVER_PORT" -sTCP:LISTEN 2>/dev/null || true)
|
||||
if [ -n "${port_pids:-}" ]; then
|
||||
log_info "未发现脚本托管的后端进程,但端口 $SERVER_PORT 被其他进程占用: $port_pids"
|
||||
else
|
||||
log_info "后端未运行"
|
||||
fi
|
||||
@@ -190,6 +226,7 @@ main() {
|
||||
ensure_command mvn
|
||||
ensure_command lsof
|
||||
ensure_command pgrep
|
||||
ensure_command ps
|
||||
ensure_command tail
|
||||
|
||||
action="${1:-restart}"
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# 后端启停脚本停机范围收敛实施记录
|
||||
|
||||
## 修改目标
|
||||
|
||||
- 修复 `bin/restart_java_backend.sh` 在执行 `stop` / `restart` 时可能误杀非脚本托管 Java 进程的问题
|
||||
- 保持现有 `java -jar ruoyi-admin.jar` 启动方式不变,仅收敛停机识别范围
|
||||
|
||||
## 根因分析
|
||||
|
||||
- 原脚本在 `stop_backend()` 中通过 `collect_pids()` 同时合并以下来源后统一执行 `kill`:
|
||||
- `logs/backend-java.pid` 中记录的 PID
|
||||
- 监听 `62318` 端口的进程
|
||||
- 命令行中包含 `ruoyi-admin.jar` 关键字的进程
|
||||
- 该逻辑会把“不是由本脚本拉起、但恰好占用端口或命中关键字”的外部进程也纳入停机列表,导致关闭后端时误杀其他进程。
|
||||
|
||||
## 修改内容
|
||||
|
||||
- 调整 [`bin/restart_java_backend.sh`](/Users/wkc/Desktop/ccdi/ccdi/bin/restart_java_backend.sh)
|
||||
- 新增 `APP_MARKER="-Dccdi.backend.root=$ROOT_DIR"`,启动时把仓库根路径标记写入 Java 启动参数
|
||||
- 新增 `is_managed_backend_pid()`,只把带有脚本标记的 Java 进程视为脚本托管进程
|
||||
- `collect_pids()` 改为仅收集:
|
||||
- `PID_FILE` 中仍存活且校验通过的进程
|
||||
- 命令行里带脚本标记的进程
|
||||
- 不再把“端口占用者”或“仅命中 jar 名的进程”直接纳入停机目标
|
||||
- `status` 增加提示:若未发现脚本托管进程,但 `62318` 被其他进程占用,会明确输出占用 PID,避免误判为脚本自身后端
|
||||
- 调整 [`docs/tests/scripts/test-restart-java-backend.sh`](/Users/wkc/Desktop/ccdi/ccdi/docs/tests/scripts/test-restart-java-backend.sh)
|
||||
- 增加对 `APP_MARKER` 的静态校验,防止后续移除托管标记
|
||||
- 新增 [`docs/tests/scripts/test-restart-java-backend-stop-scope.sh`](/Users/wkc/Desktop/ccdi/ccdi/docs/tests/scripts/test-restart-java-backend-stop-scope.sh)
|
||||
- 在临时目录复制启停脚本
|
||||
- 启动一个未托管的外部占口进程
|
||||
- 验证执行 `stop` 后该外部进程仍然存活,防止回归到误杀外部进程的行为
|
||||
|
||||
## 验证记录
|
||||
|
||||
- 执行 `sh docs/tests/scripts/test-restart-java-backend-stop-scope.sh`
|
||||
- 结果:通过
|
||||
- 说明:已验证 `stop` 不会误杀未由脚本托管的占口进程
|
||||
- 执行 `sh docs/tests/scripts/test-restart-java-backend.sh`
|
||||
- 结果:通过
|
||||
- 说明:已验证脚本仍使用 `java -jar ruoyi-admin.jar` 启动,且保留托管进程标记
|
||||
- 执行 `sh -n bin/restart_java_backend.sh`
|
||||
- 结果:通过
|
||||
- 说明:脚本语法正确
|
||||
|
||||
## 影响范围
|
||||
|
||||
- 仅影响本地后端启停辅助脚本与对应测试脚本
|
||||
- 不涉及 Java 业务代码、数据库、前端页面及 Mock 服务逻辑
|
||||
57
docs/tests/scripts/test-restart-java-backend-stop-scope.sh
Normal file
57
docs/tests/scripts/test-restart-java-backend-stop-scope.sh
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/../../.." && pwd)
|
||||
SOURCE_SCRIPT="$ROOT_DIR/bin/restart_java_backend.sh"
|
||||
TMP_DIR=$(mktemp -d)
|
||||
TEST_ROOT="$TMP_DIR/app"
|
||||
TEST_BIN_DIR="$TEST_ROOT/bin"
|
||||
TEST_LOG_DIR="$TEST_ROOT/logs"
|
||||
TEST_PORT=62481
|
||||
EXTERNAL_PID=""
|
||||
|
||||
cleanup() {
|
||||
if [ -n "${EXTERNAL_PID:-}" ] && kill -0 "$EXTERNAL_PID" 2>/dev/null; then
|
||||
kill "$EXTERNAL_PID" 2>/dev/null || true
|
||||
wait "$EXTERNAL_PID" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
mkdir -p "$TEST_BIN_DIR" "$TEST_LOG_DIR"
|
||||
cp "$SOURCE_SCRIPT" "$TEST_BIN_DIR/restart_java_backend.sh"
|
||||
chmod +x "$TEST_BIN_DIR/restart_java_backend.sh"
|
||||
|
||||
python3 -m http.server "$TEST_PORT" --bind 127.0.0.1 > "$TMP_DIR/external.log" 2>&1 &
|
||||
EXTERNAL_PID=$!
|
||||
|
||||
sleep 1
|
||||
|
||||
if ! kill -0 "$EXTERNAL_PID" 2>/dev/null; then
|
||||
echo "失败: 未能启动外部占口进程"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python3 - "$TEST_BIN_DIR/restart_java_backend.sh" "$TEST_PORT" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
script_path = Path(sys.argv[1])
|
||||
port = sys.argv[2]
|
||||
content = script_path.read_text(encoding="utf-8")
|
||||
content = content.replace("SERVER_PORT=62318", f"SERVER_PORT={port}")
|
||||
script_path.write_text(content, encoding="utf-8")
|
||||
PY
|
||||
|
||||
echo "[检查] stop 不能误杀未由脚本托管的占口进程"
|
||||
sh "$TEST_BIN_DIR/restart_java_backend.sh" stop >/dev/null 2>&1 || true
|
||||
|
||||
if ! kill -0 "$EXTERNAL_PID" 2>/dev/null; then
|
||||
echo "失败: stop 误杀了未托管的外部进程"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "通过"
|
||||
@@ -27,8 +27,8 @@ if grep -Fq 'spring-boot:run' "$SCRIPT_FILE"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -Fq 'APP_KEYWORD="$JAR_NAME"' "$SCRIPT_FILE"; then
|
||||
echo "失败: 进程识别未切换到 jar 关键字"
|
||||
if ! grep -Fq 'APP_MARKER="-Dccdi.backend.root=$ROOT_DIR"' "$SCRIPT_FILE"; then
|
||||
echo "失败: 未为脚本托管的后端进程写入唯一标记"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user