Files
ccdi/deploy/start-java-backend-prod.sh
2026-04-28 17:27:24 +08:00

311 lines
7.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 "$@"