311 lines
7.1 KiB
Bash
311 lines
7.1 KiB
Bash
|
|
#!/bin/bash
|
|||
|
|
|
|||
|
|
set -euo pipefail
|
|||
|
|
|
|||
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||
|
|
|
|||
|
|
# ==================== 生产配置区:按服务器实际路径修改 ====================
|
|||
|
|
|
|||
|
|
# JDK 安装目录。留空时使用服务器已有 JAVA_HOME;仍为空时使用 PATH 中的 java。
|
|||
|
|
BACKEND_JAVA_HOME=""
|
|||
|
|
|
|||
|
|
# 后端 Jar 所在目录。生产目录结构为:启动脚本在外层,Jar 位于 backend/ruoyi-admin.jar。
|
|||
|
|
APP_HOME="${SCRIPT_DIR}/backend"
|
|||
|
|
|
|||
|
|
# 后端 Jar 文件名。
|
|||
|
|
JAR_NAME="ruoyi-admin.jar"
|
|||
|
|
|
|||
|
|
# Spring Profile。
|
|||
|
|
SPRING_PROFILES_ACTIVE="uat"
|
|||
|
|
|
|||
|
|
# JVM 参数。
|
|||
|
|
JAVA_OPTS="-Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError"
|
|||
|
|
|
|||
|
|
# 额外应用启动参数,例如:--server.port=8080
|
|||
|
|
APP_ARGS=""
|
|||
|
|
|
|||
|
|
# 停止进程等待秒数。
|
|||
|
|
STOP_WAIT_SECONDS=30
|
|||
|
|
|
|||
|
|
# ==================== 以下为脚本逻辑,一般不需要修改 ====================
|
|||
|
|
|
|||
|
|
if [[ "${APP_HOME}" != /* ]]; then
|
|||
|
|
APP_HOME="${SCRIPT_DIR}/${APP_HOME}"
|
|||
|
|
fi
|
|||
|
|
JAR_PATH="${APP_HOME}/${JAR_NAME}"
|
|||
|
|
RELATIVE_JAR_PATH=""
|
|||
|
|
if [[ "${APP_HOME}" == "${SCRIPT_DIR}/"* ]]; then
|
|||
|
|
RELATIVE_JAR_PATH="${APP_HOME#${SCRIPT_DIR}/}/${JAR_NAME}"
|
|||
|
|
fi
|
|||
|
|
LOG_DIR="${APP_HOME}/logs"
|
|||
|
|
CONSOLE_LOG="${LOG_DIR}/backend-console.log"
|
|||
|
|
PID_FILE="${LOG_DIR}/backend-java.pid"
|
|||
|
|
APP_MARKER="-Dccdi.backend.prod.home=${APP_HOME}"
|
|||
|
|
JAVA_CMD="java"
|
|||
|
|
|
|||
|
|
timestamp() {
|
|||
|
|
date "+%Y-%m-%d %H:%M:%S"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
log_info() {
|
|||
|
|
printf '[%s] %s\n' "$(timestamp)" "$1"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
log_error() {
|
|||
|
|
printf '[%s] %s\n' "$(timestamp)" "$1" >&2
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
usage() {
|
|||
|
|
cat <<'EOF'
|
|||
|
|
用法: ./start-java-backend-prod.sh [start|stop|restart|status|logs]
|
|||
|
|
|
|||
|
|
默认动作:
|
|||
|
|
start 先关闭旧后端进程,再启动生产后端 Jar,启动成功后持续输出控制台日志
|
|||
|
|
|
|||
|
|
常用配置:
|
|||
|
|
配置统一写在脚本顶部“生产配置区”,包括 BACKEND_JAVA_HOME、APP_HOME、SPRING_PROFILES_ACTIVE、JAVA_OPTS。
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
./start-java-backend-prod.sh restart
|
|||
|
|
EOF
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ensure_command() {
|
|||
|
|
local command_name="$1"
|
|||
|
|
if ! command -v "${command_name}" >/dev/null 2>&1; then
|
|||
|
|
log_error "缺少命令: ${command_name}"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
resolve_java_cmd() {
|
|||
|
|
local configured_java_home="${BACKEND_JAVA_HOME}"
|
|||
|
|
if [[ -z "${configured_java_home}" ]]; then
|
|||
|
|
configured_java_home="${JAVA_HOME:-}"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
if [[ -n "${configured_java_home}" ]]; then
|
|||
|
|
configured_java_home="${configured_java_home%/}"
|
|||
|
|
if [[ ! -x "${configured_java_home}/bin/java" ]]; then
|
|||
|
|
log_error "配置的 JAVA_HOME 无效,未找到可执行文件: ${configured_java_home}/bin/java"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
export JAVA_HOME="${configured_java_home}"
|
|||
|
|
JAVA_CMD="${JAVA_HOME}/bin/java"
|
|||
|
|
else
|
|||
|
|
ensure_command "java"
|
|||
|
|
JAVA_CMD="java"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
log_info "使用 Java 命令: ${JAVA_CMD}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
get_process_table() {
|
|||
|
|
local process_table
|
|||
|
|
if ! process_table="$(ps -ef 2>/dev/null)"; then
|
|||
|
|
log_error "执行 ps -ef 失败,无法扫描旧进程"
|
|||
|
|
return 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
printf '%s\n' "${process_table}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
is_managed_pid() {
|
|||
|
|
local pid="$1"
|
|||
|
|
if [[ -z "${pid}" ]] || ! kill -0 "${pid}" 2>/dev/null; then
|
|||
|
|
return 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
local process_table
|
|||
|
|
if ! process_table="$(get_process_table)"; then
|
|||
|
|
return 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
local line
|
|||
|
|
while IFS= read -r line; do
|
|||
|
|
set -- ${line}
|
|||
|
|
if [[ "${2:-}" == "${pid}" ]] && is_backend_process_line "${line}"; then
|
|||
|
|
return 0
|
|||
|
|
fi
|
|||
|
|
done <<<"${process_table}"
|
|||
|
|
|
|||
|
|
return 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
is_backend_process_line() {
|
|||
|
|
local line="$1"
|
|||
|
|
[[ "${line}" != *"<defunct>"* ]] || return 1
|
|||
|
|
[[ "${line}" == *" -jar ${JAR_PATH}"* ]] && return 0
|
|||
|
|
[[ -n "${RELATIVE_JAR_PATH}" && "${line}" == *" -jar ${RELATIVE_JAR_PATH}"* ]]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
collect_pids() {
|
|||
|
|
local all_pids=""
|
|||
|
|
local pid
|
|||
|
|
local process_table
|
|||
|
|
if ! process_table="$(get_process_table)"; then
|
|||
|
|
return 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
if [[ -f "${PID_FILE}" ]]; then
|
|||
|
|
pid="$(cat "${PID_FILE}" 2>/dev/null || true)"
|
|||
|
|
if is_managed_pid "${pid}"; then
|
|||
|
|
all_pids="${all_pids} ${pid}"
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
local line
|
|||
|
|
while IFS= read -r line; do
|
|||
|
|
set -- ${line}
|
|||
|
|
pid="${2:-}"
|
|||
|
|
if [[ "${pid}" =~ ^[0-9]+$ ]] && is_backend_process_line "${line}"; then
|
|||
|
|
all_pids="${all_pids} ${pid}"
|
|||
|
|
fi
|
|||
|
|
done <<<"${process_table}"
|
|||
|
|
|
|||
|
|
local unique_pids=""
|
|||
|
|
for pid in ${all_pids}; do
|
|||
|
|
case " ${unique_pids} " in
|
|||
|
|
*" ${pid} "*) ;;
|
|||
|
|
*) unique_pids="${unique_pids} ${pid}" ;;
|
|||
|
|
esac
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
xargs <<<"${unique_pids}" 2>/dev/null || true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
start_backend() {
|
|||
|
|
resolve_java_cmd
|
|||
|
|
|
|||
|
|
if [[ ! -f "${JAR_PATH}" ]]; then
|
|||
|
|
log_error "未找到后端 Jar: ${JAR_PATH}"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
local running_pids
|
|||
|
|
running_pids="$(collect_pids)"
|
|||
|
|
if [[ -n "${running_pids}" ]]; then
|
|||
|
|
log_error "检测到后端已在运行: ${running_pids}"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
mkdir -p "${LOG_DIR}"
|
|||
|
|
printf '\n===== %s start =====\n' "$(timestamp)" >>"${CONSOLE_LOG}"
|
|||
|
|
|
|||
|
|
local profile_arg=""
|
|||
|
|
if [[ -n "${SPRING_PROFILES_ACTIVE}" ]]; then
|
|||
|
|
profile_arg="--spring.profiles.active=${SPRING_PROFILES_ACTIVE}"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
log_info "开始启动后端 Jar: ${JAR_PATH}"
|
|||
|
|
nohup "${JAVA_CMD}" "${APP_MARKER}" ${JAVA_OPTS} -jar "${JAR_PATH}" ${profile_arg} ${APP_ARGS} >>"${CONSOLE_LOG}" 2>&1 &
|
|||
|
|
echo $! >"${PID_FILE}"
|
|||
|
|
|
|||
|
|
sleep 3
|
|||
|
|
|
|||
|
|
local starter_pid
|
|||
|
|
starter_pid="$(cat "${PID_FILE}" 2>/dev/null || true)"
|
|||
|
|
if [[ -z "${starter_pid}" ]] || ! kill -0 "${starter_pid}" 2>/dev/null; then
|
|||
|
|
log_error "启动命令未保持运行,请检查日志: ${CONSOLE_LOG}"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
log_info "后端启动完成,PID: ${starter_pid}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
stop_backend() {
|
|||
|
|
local pids
|
|||
|
|
pids="$(collect_pids)"
|
|||
|
|
|
|||
|
|
if [[ -z "${pids}" ]]; then
|
|||
|
|
log_info "未发现运行中的后端进程"
|
|||
|
|
rm -f "${PID_FILE}"
|
|||
|
|
return 0
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
log_info "准备停止后端进程: ${pids}"
|
|||
|
|
local pid
|
|||
|
|
for pid in ${pids}; do
|
|||
|
|
kill -TERM "${pid}" 2>/dev/null || true
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
local elapsed=0
|
|||
|
|
local remaining_pids="${pids}"
|
|||
|
|
while [[ -n "${remaining_pids}" && "${elapsed}" -lt "${STOP_WAIT_SECONDS}" ]]; do
|
|||
|
|
sleep 1
|
|||
|
|
elapsed=$((elapsed + 1))
|
|||
|
|
remaining_pids=""
|
|||
|
|
for pid in ${pids}; do
|
|||
|
|
if kill -0 "${pid}" 2>/dev/null; then
|
|||
|
|
remaining_pids="${remaining_pids} ${pid}"
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
remaining_pids="$(xargs <<<"${remaining_pids}" 2>/dev/null || true)"
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
if [[ -n "${remaining_pids}" ]]; then
|
|||
|
|
log_info "仍有进程未退出,执行强制停止: ${remaining_pids}"
|
|||
|
|
for pid in ${remaining_pids}; do
|
|||
|
|
kill -KILL "${pid}" 2>/dev/null || true
|
|||
|
|
done
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
rm -f "${PID_FILE}"
|
|||
|
|
log_info "后端停止完成"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
status_backend() {
|
|||
|
|
local pids
|
|||
|
|
pids="$(collect_pids)"
|
|||
|
|
if [[ -n "${pids}" ]]; then
|
|||
|
|
log_info "后端正在运行,进程: ${pids}"
|
|||
|
|
return 0
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
log_info "后端未运行"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
follow_logs() {
|
|||
|
|
mkdir -p "${LOG_DIR}"
|
|||
|
|
touch "${CONSOLE_LOG}"
|
|||
|
|
log_info "持续输出日志中,按 Ctrl+C 仅退出日志查看,不会停止后端进程"
|
|||
|
|
tail -n 200 -F "${CONSOLE_LOG}"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
start_action() {
|
|||
|
|
stop_backend
|
|||
|
|
start_backend
|
|||
|
|
follow_logs
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main() {
|
|||
|
|
local action="${1:-start}"
|
|||
|
|
case "${action}" in
|
|||
|
|
start)
|
|||
|
|
start_action
|
|||
|
|
;;
|
|||
|
|
stop)
|
|||
|
|
stop_backend
|
|||
|
|
;;
|
|||
|
|
restart)
|
|||
|
|
start_action
|
|||
|
|
;;
|
|||
|
|
status)
|
|||
|
|
status_backend
|
|||
|
|
;;
|
|||
|
|
logs)
|
|||
|
|
follow_logs
|
|||
|
|
;;
|
|||
|
|
-h|--help|help)
|
|||
|
|
usage
|
|||
|
|
;;
|
|||
|
|
*)
|
|||
|
|
usage
|
|||
|
|
exit 1
|
|||
|
|
;;
|
|||
|
|
esac
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main "$@"
|