| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- #!/bin/bash
- #===============================================================================
- # RuoYi-Vue-Plus 风控平台 - 服务管理脚本
- # 用法: ./server.sh {start|stop|restart|status|logs}
- #===============================================================================
- set -e
- # ==================== 配置区(按需修改) ====================
- # 应用名称
- APP_NAME="risk-control-platform"
- # JAR 包路径(相对于脚本目录)
- JAR_PATH="ruoyi-admin/target/ruoyi-admin.jar"
- # Spring 激活环境(dev / prod / test)
- PROFILE="${SPRING_PROFILE:-prod}"
- # 服务端口(用于健康检查)
- SERVER_PORT=9224
- # PID 文件
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
- PID_FILE="$SCRIPT_DIR/${APP_NAME}.pid"
- # 日志文件
- LOG_DIR="$SCRIPT_DIR/logs"
- LOG_FILE="$LOG_DIR/${APP_NAME}.log"
- GC_LOG_FILE="$LOG_DIR/gc.log"
- # JVM 参数(直接指定 JDK 路径,避免环境变量丢失问题)
- JAVA_HOME="/usr/local/graalvm-jdk-25.0.2"
- JAVA_OPTS="-Xms512m -Xmx2048m -Xmn512m"
- JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
- JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$LOG_DIR"
- # JDK 9+ 统一 GC 日志格式,不再使用 PrintGCDetails/PrintGCDateStamps/Xloggc
- JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:file=$GC_LOG_FILE:time,level,tags:filecount=5,filesize=50M"
- JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8 -Djava.awt.headless=true"
- # 优雅停机等待时间(秒)
- GRACEFUL_TIMEOUT=30
- # 启动后健康检查重试次数与间隔(秒)
- HEALTH_RETRIES=30
- HEALTH_INTERVAL=2
- # ==================== 函数定义 ====================
- # 获取 Java 命令(优先使用 JAVA_HOME,fallback 到系统 PATH)
- get_java() {
- if [ -x "$JAVA_HOME/bin/java" ]; then
- echo "$JAVA_HOME/bin/java"
- return 0
- fi
- # fallback 到系统 java
- local sys_java
- sys_java=$(command -v java 2>/dev/null)
- if [ -n "$sys_java" ]; then
- echo "[WARN] $JAVA_HOME/bin/java 不存在或不可执行,fallback 到 $sys_java" >&2
- echo "$sys_java"
- return 0
- fi
- return 1
- }
- # 检查 Java 版本
- check_java() {
- local java_cmd
- java_cmd=$(get_java)
- if [ -z "$java_cmd" ] || ! command -v "$java_cmd" &>/dev/null; then
- echo "[ERROR] Java 未找到"
- echo "[HINT] 请设置 JAVA_HOME 或将 java 加入 PATH"
- echo " 例如: export JAVA_HOME=/usr/local/java && ./server.sh start"
- exit 1
- fi
- local full_version
- full_version=$("$java_cmd" -version 2>&1 | head -1)
- echo "[INFO] Java 命令: $java_cmd"
- echo "[INFO] Java 版本: $full_version"
- # 提取主版本号(兼容 Linux/macOS,处理 1.8、9、11、17、21、25 等格式)
- local version
- version=$(echo "$full_version" | sed 's/.*version "//' | sed 's/\..*//')
- # JDK 1.8 格式: version="1.8.0_402" → 提取后为 "1" → 需改为 "8"
- if [ "$version" = "1" ]; then
- version=$(echo "$full_version" | sed 's/.*version "1\.//' | sed 's/\..*//')
- fi
- if [ -z "$version" ] || ! echo "$version" | grep -qE '^[0-9]+$'; then
- echo "[ERROR] 无法解析 Java 版本号"
- exit 1
- fi
- if [ "$version" -lt 17 ]; then
- echo "[ERROR] 需要 Java 17+,当前主版本: $version"
- echo "[HINT] 当前使用的是 JDK 8,请安装 JDK 17+ 并设置 JAVA_HOME:"
- echo " export JAVA_HOME=/usr/local/graalvm-jdk-25.0.2 && ./server.sh start"
- exit 1
- fi
- echo "[INFO] Java 版本检查通过 (主版本: $version)"
- }
- # 获取进程 PID
- get_pid() {
- if [ -f "$PID_FILE" ]; then
- local pid
- pid=$(cat "$PID_FILE")
- if ps -p "$pid" &>/dev/null; then
- echo "$pid"
- return 0
- fi
- fi
- # 备选:通过端口找 PID
- local port_pid
- port_pid=$(lsof -t -i:"$SERVER_PORT" -sTCP:LISTEN 2>/dev/null | head -1)
- if [ -n "$port_pid" ]; then
- echo "$port_pid"
- return 0
- fi
- return 1
- }
- # 检查是否运行中
- is_running() {
- local pid
- pid=$(get_pid 2>/dev/null)
- [ -n "$pid" ]
- }
- # 启动服务
- start() {
- if is_running; then
- local pid
- pid=$(get_pid)
- echo "[INFO] $APP_NAME 已在运行 (PID: $pid)"
- return 0
- fi
- check_java
- # 确保 JAR 存在
- if [ ! -f "$SCRIPT_DIR/$JAR_PATH" ]; then
- echo "[ERROR] JAR 包不存在: $SCRIPT_DIR/$JAR_PATH"
- echo "[HINT] 请先执行: mvn clean package -DskipTests"
- exit 1
- fi
- # 创建日志目录
- mkdir -p "$LOG_DIR"
- local java_cmd
- java_cmd=$(get_java)
- echo "[INFO] 正在启动 $APP_NAME ..."
- echo "[INFO] Profile: $PROFILE | 端口: $SERVER_PORT"
- nohup "$java_cmd" $JAVA_OPTS \
- -jar "$SCRIPT_DIR/$JAR_PATH" \
- --spring.profiles.active="$PROFILE" \
- --server.port="$SERVER_PORT" \
- >> "$LOG_FILE" 2>&1 &
- local new_pid=$!
- echo "$new_pid" > "$PID_FILE"
- echo "[INFO] 进程已启动 (PID: $new_pid),等待服务就绪..."
- # 健康检查
- local retries=0
- while [ $retries -lt $HEALTH_RETRIES ]; do
- sleep $HEALTH_INTERVAL
- if ! ps -p "$new_pid" &>/dev/null; then
- echo "[ERROR] 进程已退出,请查看日志: $LOG_FILE"
- tail -20 "$LOG_FILE"
- exit 1
- fi
- if curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:$SERVER_PORT/" 2>/dev/null | grep -qE "^(200|302|401|403)$"; then
- echo "[OK] $APP_NAME 启动成功 (PID: $new_pid, 端口: $SERVER_PORT)"
- return 0
- fi
- retries=$((retries + 1))
- echo -n "."
- done
- echo ""
- echo "[WARN] 端口 $SERVER_PORT 在 ${HEALTH_RETRIES} 次重试后仍未响应,但进程存活"
- echo "[INFO] 请查看日志: tail -f $LOG_FILE"
- }
- # 停止服务
- stop() {
- local pid
- pid=$(get_pid 2>/dev/null)
- if [ -z "$pid" ]; then
- echo "[INFO] $APP_NAME 未运行"
- rm -f "$PID_FILE"
- return 0
- fi
- echo "[INFO] 正在停止 $APP_NAME (PID: $pid) ..."
- # 优雅停机
- kill "$pid" 2>/dev/null || true
- local waited=0
- while [ $waited -lt $GRACEFUL_TIMEOUT ]; do
- if ! ps -p "$pid" &>/dev/null; then
- echo "[OK] $APP_NAME 已停止"
- rm -f "$PID_FILE"
- return 0
- fi
- sleep 1
- waited=$((waited + 1))
- done
- # 强制终止
- echo "[WARN] 优雅停机超时,强制终止..."
- kill -9 "$pid" 2>/dev/null || true
- sleep 2
- if ! ps -p "$pid" &>/dev/null; then
- echo "[OK] $APP_NAME 已强制停止"
- else
- echo "[ERROR] 无法停止 PID: $pid"
- exit 1
- fi
- rm -f "$PID_FILE"
- }
- # 重启服务
- restart() {
- echo "[INFO] ====== 重启 $APP_NAME ======"
- stop
- sleep 2
- start
- }
- # 查看状态
- status() {
- if is_running; then
- local pid
- pid=$(get_pid)
- echo "[INFO] $APP_NAME 运行中"
- echo " PID: $pid"
- echo " 端口: $SERVER_PORT"
- echo " 启动时间: $(ps -o lstart= -p "$pid" 2>/dev/null)"
- echo " 内存: $(ps -o rss= -p "$pid" 2>/dev/null | awk '{printf "%.0f MB", $1/1024}')"
- else
- echo "[INFO] $APP_NAME 未运行"
- fi
- }
- # 查看日志
- logs() {
- if [ -f "$LOG_FILE" ]; then
- tail -f "$LOG_FILE"
- else
- echo "[INFO] 日志文件不存在: $LOG_FILE"
- fi
- }
- # ==================== 主入口 ====================
- case "${1:-}" in
- start)
- start
- ;;
- stop)
- stop
- ;;
- restart)
- restart
- ;;
- status)
- status
- ;;
- logs)
- logs
- ;;
- *)
- echo "用法: $0 {start|stop|restart|status|logs}"
- echo ""
- echo " start - 启动服务"
- echo " stop - 停止服务"
- echo " restart - 重启服务"
- echo " status - 查看运行状态"
- echo " logs - 实时查看日志"
- echo ""
- echo "环境变量:"
- echo " JAVA_HOME - JDK 路径 (默认: /usr/local/graalvm-jdk-25.0.2)"
- echo " SPRING_PROFILE - 环境 (默认: prod)"
- echo ""
- exit 1
- ;;
- esac
|