server.sh 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #!/bin/bash
  2. #===============================================================================
  3. # RuoYi-Vue-Plus 风控平台 - 服务管理脚本
  4. # 用法: ./server.sh {start|stop|restart|status|logs}
  5. #===============================================================================
  6. set -e
  7. # ==================== 配置区(按需修改) ====================
  8. # 应用名称
  9. APP_NAME="risk-control-platform"
  10. # JAR 包路径(相对于脚本目录)
  11. JAR_PATH="ruoyi-admin/target/ruoyi-admin.jar"
  12. # Spring 激活环境(dev / prod / test)
  13. PROFILE="${SPRING_PROFILE:-prod}"
  14. # 服务端口(用于健康检查)
  15. SERVER_PORT=9224
  16. # PID 文件
  17. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
  18. PID_FILE="$SCRIPT_DIR/${APP_NAME}.pid"
  19. # 日志文件
  20. LOG_DIR="$SCRIPT_DIR/logs"
  21. LOG_FILE="$LOG_DIR/${APP_NAME}.log"
  22. GC_LOG_FILE="$LOG_DIR/gc.log"
  23. # JVM 参数(直接指定 JDK 路径,避免环境变量丢失问题)
  24. JAVA_HOME="/usr/local/graalvm-jdk-25.0.2"
  25. JAVA_OPTS="-Xms512m -Xmx2048m -Xmn512m"
  26. JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
  27. JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$LOG_DIR"
  28. # JDK 9+ 统一 GC 日志格式,不再使用 PrintGCDetails/PrintGCDateStamps/Xloggc
  29. JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:file=$GC_LOG_FILE:time,level,tags:filecount=5,filesize=50M"
  30. JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8 -Djava.awt.headless=true"
  31. # 优雅停机等待时间(秒)
  32. GRACEFUL_TIMEOUT=30
  33. # 启动后健康检查重试次数与间隔(秒)
  34. HEALTH_RETRIES=30
  35. HEALTH_INTERVAL=2
  36. # ==================== 函数定义 ====================
  37. # 获取 Java 命令(优先使用 JAVA_HOME,fallback 到系统 PATH)
  38. get_java() {
  39. if [ -x "$JAVA_HOME/bin/java" ]; then
  40. echo "$JAVA_HOME/bin/java"
  41. return 0
  42. fi
  43. # fallback 到系统 java
  44. local sys_java
  45. sys_java=$(command -v java 2>/dev/null)
  46. if [ -n "$sys_java" ]; then
  47. echo "[WARN] $JAVA_HOME/bin/java 不存在或不可执行,fallback 到 $sys_java" >&2
  48. echo "$sys_java"
  49. return 0
  50. fi
  51. return 1
  52. }
  53. # 检查 Java 版本
  54. check_java() {
  55. local java_cmd
  56. java_cmd=$(get_java)
  57. if [ -z "$java_cmd" ] || ! command -v "$java_cmd" &>/dev/null; then
  58. echo "[ERROR] Java 未找到"
  59. echo "[HINT] 请设置 JAVA_HOME 或将 java 加入 PATH"
  60. echo " 例如: export JAVA_HOME=/usr/local/java && ./server.sh start"
  61. exit 1
  62. fi
  63. local full_version
  64. full_version=$("$java_cmd" -version 2>&1 | head -1)
  65. echo "[INFO] Java 命令: $java_cmd"
  66. echo "[INFO] Java 版本: $full_version"
  67. # 提取主版本号(兼容 Linux/macOS,处理 1.8、9、11、17、21、25 等格式)
  68. local version
  69. version=$(echo "$full_version" | sed 's/.*version "//' | sed 's/\..*//')
  70. # JDK 1.8 格式: version="1.8.0_402" → 提取后为 "1" → 需改为 "8"
  71. if [ "$version" = "1" ]; then
  72. version=$(echo "$full_version" | sed 's/.*version "1\.//' | sed 's/\..*//')
  73. fi
  74. if [ -z "$version" ] || ! echo "$version" | grep -qE '^[0-9]+$'; then
  75. echo "[ERROR] 无法解析 Java 版本号"
  76. exit 1
  77. fi
  78. if [ "$version" -lt 17 ]; then
  79. echo "[ERROR] 需要 Java 17+,当前主版本: $version"
  80. echo "[HINT] 当前使用的是 JDK 8,请安装 JDK 17+ 并设置 JAVA_HOME:"
  81. echo " export JAVA_HOME=/usr/local/graalvm-jdk-25.0.2 && ./server.sh start"
  82. exit 1
  83. fi
  84. echo "[INFO] Java 版本检查通过 (主版本: $version)"
  85. }
  86. # 获取进程 PID
  87. get_pid() {
  88. if [ -f "$PID_FILE" ]; then
  89. local pid
  90. pid=$(cat "$PID_FILE")
  91. if ps -p "$pid" &>/dev/null; then
  92. echo "$pid"
  93. return 0
  94. fi
  95. fi
  96. # 备选:通过端口找 PID
  97. local port_pid
  98. port_pid=$(lsof -t -i:"$SERVER_PORT" -sTCP:LISTEN 2>/dev/null | head -1)
  99. if [ -n "$port_pid" ]; then
  100. echo "$port_pid"
  101. return 0
  102. fi
  103. return 1
  104. }
  105. # 检查是否运行中
  106. is_running() {
  107. local pid
  108. pid=$(get_pid 2>/dev/null)
  109. [ -n "$pid" ]
  110. }
  111. # 启动服务
  112. start() {
  113. if is_running; then
  114. local pid
  115. pid=$(get_pid)
  116. echo "[INFO] $APP_NAME 已在运行 (PID: $pid)"
  117. return 0
  118. fi
  119. check_java
  120. # 确保 JAR 存在
  121. if [ ! -f "$SCRIPT_DIR/$JAR_PATH" ]; then
  122. echo "[ERROR] JAR 包不存在: $SCRIPT_DIR/$JAR_PATH"
  123. echo "[HINT] 请先执行: mvn clean package -DskipTests"
  124. exit 1
  125. fi
  126. # 创建日志目录
  127. mkdir -p "$LOG_DIR"
  128. local java_cmd
  129. java_cmd=$(get_java)
  130. echo "[INFO] 正在启动 $APP_NAME ..."
  131. echo "[INFO] Profile: $PROFILE | 端口: $SERVER_PORT"
  132. nohup "$java_cmd" $JAVA_OPTS \
  133. -jar "$SCRIPT_DIR/$JAR_PATH" \
  134. --spring.profiles.active="$PROFILE" \
  135. --server.port="$SERVER_PORT" \
  136. >> "$LOG_FILE" 2>&1 &
  137. local new_pid=$!
  138. echo "$new_pid" > "$PID_FILE"
  139. echo "[INFO] 进程已启动 (PID: $new_pid),等待服务就绪..."
  140. # 健康检查
  141. local retries=0
  142. while [ $retries -lt $HEALTH_RETRIES ]; do
  143. sleep $HEALTH_INTERVAL
  144. if ! ps -p "$new_pid" &>/dev/null; then
  145. echo "[ERROR] 进程已退出,请查看日志: $LOG_FILE"
  146. tail -20 "$LOG_FILE"
  147. exit 1
  148. fi
  149. 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
  150. echo "[OK] $APP_NAME 启动成功 (PID: $new_pid, 端口: $SERVER_PORT)"
  151. return 0
  152. fi
  153. retries=$((retries + 1))
  154. echo -n "."
  155. done
  156. echo ""
  157. echo "[WARN] 端口 $SERVER_PORT 在 ${HEALTH_RETRIES} 次重试后仍未响应,但进程存活"
  158. echo "[INFO] 请查看日志: tail -f $LOG_FILE"
  159. }
  160. # 停止服务
  161. stop() {
  162. local pid
  163. pid=$(get_pid 2>/dev/null)
  164. if [ -z "$pid" ]; then
  165. echo "[INFO] $APP_NAME 未运行"
  166. rm -f "$PID_FILE"
  167. return 0
  168. fi
  169. echo "[INFO] 正在停止 $APP_NAME (PID: $pid) ..."
  170. # 优雅停机
  171. kill "$pid" 2>/dev/null || true
  172. local waited=0
  173. while [ $waited -lt $GRACEFUL_TIMEOUT ]; do
  174. if ! ps -p "$pid" &>/dev/null; then
  175. echo "[OK] $APP_NAME 已停止"
  176. rm -f "$PID_FILE"
  177. return 0
  178. fi
  179. sleep 1
  180. waited=$((waited + 1))
  181. done
  182. # 强制终止
  183. echo "[WARN] 优雅停机超时,强制终止..."
  184. kill -9 "$pid" 2>/dev/null || true
  185. sleep 2
  186. if ! ps -p "$pid" &>/dev/null; then
  187. echo "[OK] $APP_NAME 已强制停止"
  188. else
  189. echo "[ERROR] 无法停止 PID: $pid"
  190. exit 1
  191. fi
  192. rm -f "$PID_FILE"
  193. }
  194. # 重启服务
  195. restart() {
  196. echo "[INFO] ====== 重启 $APP_NAME ======"
  197. stop
  198. sleep 2
  199. start
  200. }
  201. # 查看状态
  202. status() {
  203. if is_running; then
  204. local pid
  205. pid=$(get_pid)
  206. echo "[INFO] $APP_NAME 运行中"
  207. echo " PID: $pid"
  208. echo " 端口: $SERVER_PORT"
  209. echo " 启动时间: $(ps -o lstart= -p "$pid" 2>/dev/null)"
  210. echo " 内存: $(ps -o rss= -p "$pid" 2>/dev/null | awk '{printf "%.0f MB", $1/1024}')"
  211. else
  212. echo "[INFO] $APP_NAME 未运行"
  213. fi
  214. }
  215. # 查看日志
  216. logs() {
  217. if [ -f "$LOG_FILE" ]; then
  218. tail -f "$LOG_FILE"
  219. else
  220. echo "[INFO] 日志文件不存在: $LOG_FILE"
  221. fi
  222. }
  223. # ==================== 主入口 ====================
  224. case "${1:-}" in
  225. start)
  226. start
  227. ;;
  228. stop)
  229. stop
  230. ;;
  231. restart)
  232. restart
  233. ;;
  234. status)
  235. status
  236. ;;
  237. logs)
  238. logs
  239. ;;
  240. *)
  241. echo "用法: $0 {start|stop|restart|status|logs}"
  242. echo ""
  243. echo " start - 启动服务"
  244. echo " stop - 停止服务"
  245. echo " restart - 重启服务"
  246. echo " status - 查看运行状态"
  247. echo " logs - 实时查看日志"
  248. echo ""
  249. echo "环境变量:"
  250. echo " JAVA_HOME - JDK 路径 (默认: /usr/local/graalvm-jdk-25.0.2)"
  251. echo " SPRING_PROFILE - 环境 (默认: prod)"
  252. echo ""
  253. exit 1
  254. ;;
  255. esac