提交代码

This commit is contained in:
zlwu committed 2025-08-31 19:38:31 +08:00
1 parent 3134870b68
commit c20df56dcd
26 files changed
+2931 -37

No files matched your search

@@ -0,0 +1,135 @@
version: '3.8'
services:
# 9900端口实例
ruoyi-admin-9900:
image: openjdk:17-jre-slim
container_name: ruoyi-admin-9900
ports:
- "9900:9900"
volumes:
- ./ruoyi-admin.jar:/app/ruoyi-admin.jar
- ./logs:/app/logs
- ./temp:/app/temp
working_dir: /app
command: >
java -Xms512m -Xmx1024m
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
-Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/
-Dspring.profiles.active=prod-9900
-Dserver.port=9900
-jar ruoyi-admin.jar
environment:
- SPRING_PROFILES_ACTIVE=prod-9900
- SERVER_PORT=9900
restart: unless-stopped
networks:
- ruoyi-network
# 9901端口实例
ruoyi-admin-9901:
image: openjdk:17-jre-slim
container_name: ruoyi-admin-9901
ports:
- "9901:9901"
volumes:
- ./ruoyi-admin.jar:/app/ruoyi-admin.jar
- ./logs:/app/logs
- ./temp:/app/temp
working_dir: /app
command: >
java -Xms512m -Xmx1024m
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
-Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/
-Dspring.profiles.active=prod-9901
-Dserver.port=9901
-jar ruoyi-admin.jar
environment:
- SPRING_PROFILES_ACTIVE=prod-9901
- SERVER_PORT=9901
restart: unless-stopped
networks:
- ruoyi-network
# 9902端口实例
ruoyi-admin-9902:
image: openjdk:17-jre-slim
container_name: ruoyi-admin-9902
ports:
- "9902:9902"
volumes:
- ./ruoyi-admin.jar:/app/ruoyi-admin.jar
- ./logs:/app/logs
- ./temp:/app/temp
working_dir: /app
command: >
java -Xms512m -Xmx1024m
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
-Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/
-Dspring.profiles.active=prod-9902
-Dserver.port=9902
-jar ruoyi-admin.jar
environment:
- SPRING_PROFILES_ACTIVE=prod-9902
- SERVER_PORT=9902
restart: unless-stopped
networks:
- ruoyi-network
# 9903端口实例
ruoyi-admin-9903:
image: openjdk:17-jre-slim
container_name: ruoyi-admin-9903
ports:
- "9903:9903"
volumes:
- ./ruoyi-admin.jar:/app/ruoyi-admin.jar
- ./logs:/app/logs
- ./temp:/app/temp
working_dir: /app
command: >
java -Xms512m -Xmx1024m
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
-Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/
-Dspring.profiles.active=prod-9903
-Dserver.port=9903
-jar ruoyi-admin.jar
environment:
- SPRING_PROFILES_ACTIVE=prod-9903
- SERVER_PORT=9903
restart: unless-stopped
networks:
- ruoyi-network
# Nginx负载均衡器
nginx:
image: nginx:alpine
container_name: ruoyi-nginx
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- ruoyi-admin-9900
- ruoyi-admin-9901
- ruoyi-admin-9902
- ruoyi-admin-9903
restart: unless-stopped
networks:
- ruoyi-network
networks:
ruoyi-network:
driver: bridge
volumes:
logs:
temp:
@@ -0,0 +1,116 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
# 基础配置
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# 上游服务器配置
upstream ruoyi_backend {
# 轮询负载均衡
server ruoyi-admin-9900:9900 weight=1 max_fails=3 fail_timeout=30s;
server ruoyi-admin-9901:9901 weight=1 max_fails=3 fail_timeout=30s;
server ruoyi-admin-9902:9902 weight=1 max_fails=3 fail_timeout=30s;
server ruoyi-admin-9903:9903 weight=1 max_fails=3 fail_timeout=30s;
# 健康检查
keepalive 32;
}
# 主服务器配置
server {
listen 80;
server_name localhost;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
proxy_pass http://ruoyi_backend;
}
# API请求
location / {
proxy_pass http://ruoyi_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时设置
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# 缓冲设置
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
}
# 健康检查端点
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# 状态监控页面
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}
# 监控和日志配置
server {
listen 8080;
server_name localhost;
location / {
return 200 "Nginx Load Balancer Status\n";
add_header Content-Type text/plain;
}
location /status {
stub_status on;
access_log off;
}
}
}
@@ -0,0 +1,368 @@
#!/bin/bash
# 扩展服务启动停止一体化脚本
# 使用方法: ./start-extend-services.sh [start|stop|restart|status] [service_name]
# 服务名称: monitor, snailjob, all
# 配置变量
MONITOR_JAR="ruoyi-monitor-admin.jar"
SNAILJOB_JAR="ruoyi-snailjob-server.jar"
JAVA_OPTS="-Xms2048m -Xmx2048m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m"
JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai"
JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/"
# 服务配置
declare -A SERVICES
SERVICES[monitor]="9090:ruoyi-monitor-admin:prod"
SERVICES[snailjob]="8800:ruoyi-snailjob-server:prod"
# 日志目录
LOG_DIR="logs"
mkdir -p $LOG_DIR
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 打印带颜色的消息
print_message() {
local color=$1
local message=$2
echo -e "${color}${message}${NC}"
}
# 检查JAR文件是否存在
check_jar() {
local service=$1
local jar_file=""
case $service in
monitor)
jar_file="./$MONITOR_JAR"
;;
snailjob)
jar_file="./$SNAILJOB_JAR"
;;
esac
if [ ! -f "$jar_file" ]; then
print_message $RED "错误: $jar_file 文件不存在!"
print_message $YELLOW "请先编译项目: mvn clean package -Pprod"
return 1
fi
return 0
}
# 启动单个服务
start_service() {
local service=$1
local config=${SERVICES[$service]}
local port=$(echo $config | cut -d: -f1)
local app_name=$(echo $config | cut -d: -f2)
local profile=$(echo $config | cut -d: -f3)
local pid_file="logs/${app_name}.pid"
local log_file="logs/${app_name}.log"
# 检查是否已经运行
if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file")
if ps -p $pid > /dev/null 2>&1; then
print_message $YELLOW "服务 $service 已经在运行中 (PID: $pid)"
return
else
# PID文件存在但进程不存在,清理PID文件
rm -f "$pid_file"
fi
fi
# 检查JAR文件
if ! check_jar $service; then
return 1
fi
print_message $GREEN "正在启动服务 $service (端口: $port, 配置文件: $profile)..."
# 启动应用
case $service in
monitor)
local jar_path="../ruoyi-extend/ruoyi-monitor-admin/target/$MONITOR_JAR"
nohup java $JAVA_OPTS \
-Dspring.profiles.active=$profile \
-Dserver.port=$port \
-jar "$jar_path" \
> "$log_file" 2>&1 &
;;
snailjob)
local jar_path="../ruoyi-extend/ruoyi-snailjob-server/target/$SNAILJOB_JAR"
nohup java $JAVA_OPTS \
-Dspring.profiles.active=$profile \
-Dserver.port=$port \
-jar "$jar_path" \
> "$log_file" 2>&1 &
;;
esac
local new_pid=$!
echo $new_pid > "$pid_file"
# 等待启动
sleep 5
# 检查启动状态
if ps -p $new_pid > /dev/null 2>&1; then
print_message $GREEN "服务 $service 启动成功 (PID: $new_pid)"
print_message $GREEN "访问地址: http://localhost:$port"
print_message $GREEN "日志文件: $log_file"
# 显示服务特定信息
case $service in
monitor)
print_message $BLUE "监控中心: http://localhost:$port/admin"
;;
snailjob)
print_message $BLUE "任务调度: http://localhost:$port/snail-job"
;;
esac
else
print_message $RED "服务 $service 启动失败,请检查日志文件: $log_file"
rm -f "$pid_file"
fi
}
# 停止单个服务
stop_service() {
local service=$1
local config=${SERVICES[$service]}
local app_name=$(echo $config | cut -d: -f2)
local pid_file="logs/${app_name}.pid"
if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file")
if ps -p $pid > /dev/null 2>&1; then
print_message $YELLOW "正在停止服务 $service (PID: $pid)..."
kill $pid
# 等待进程结束
local count=0
while ps -p $pid > /dev/null 2>&1 && [ $count -lt 30 ]; do
sleep 1
count=$((count + 1))
done
# 强制杀死进程
if ps -p $pid > /dev/null 2>&1; then
print_message $YELLOW "强制停止服务 $service..."
kill -9 $pid
fi
rm -f "$pid_file"
print_message $GREEN "服务 $service 已停止"
else
print_message $YELLOW "服务 $service 未运行"
rm -f "$pid_file"
fi
else
print_message $YELLOW "服务 $service 未运行"
fi
}
# 重启单个服务
restart_service() {
local service=$1
print_message $YELLOW "重启服务 $service..."
stop_service $service
sleep 3
start_service $service
}
# 查看服务状态
status_service() {
local service=$1
local config=${SERVICES[$service]}
local port=$(echo $config | cut -d: -f1)
local app_name=$(echo $config | cut -d: -f2)
local pid_file="logs/${app_name}.pid"
if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file")
if ps -p $pid > /dev/null 2>&1; then
local memory=$(ps -o rss= -p $pid | awk '{print $1/1024 " MB"}')
local uptime=$(ps -o etime= -p $pid)
print_message $GREEN "服务 $service: 运行中 (PID: $pid, 端口: $port, 内存: $memory, 运行时间: $uptime)"
else
print_message $RED "服务 $service: 异常 (PID文件存在但进程不存在)"
rm -f "$pid_file"
fi
else
print_message $YELLOW "服务 $service: 未运行 (端口: $port)"
fi
}
# 启动所有服务
start_all() {
print_message $GREEN "启动所有扩展服务..."
for service in "${!SERVICES[@]}"; do
start_service $service
sleep 3
done
}
# 停止所有服务
stop_all() {
print_message $YELLOW "停止所有扩展服务..."
for service in "${!SERVICES[@]}"; do
stop_service $service
done
}
# 重启所有服务
restart_all() {
print_message $YELLOW "重启所有扩展服务..."
stop_all
sleep 5
start_all
}
# 查看所有服务状态
status_all() {
print_message $GREEN "所有扩展服务状态:"
for service in "${!SERVICES[@]}"; do
status_service $service
done
}
# 编译项目
build_project() {
print_message $BLUE "开始编译扩展服务项目..."
# 编译monitor-admin
print_message $YELLOW "编译 ruoyi-monitor-admin..."
cd ../ruoyi-extend/ruoyi-monitor-admin
mvn clean package -Pprod
if [ $? -eq 0 ]; then
print_message $GREEN "ruoyi-monitor-admin 编译成功"
else
print_message $RED "ruoyi-monitor-admin 编译失败"
return 1
fi
# 编译snailjob-server
print_message $YELLOW "编译 ruoyi-snailjob-server..."
cd ../ruoyi-snailjob-server
mvn clean package -Pprod
if [ $? -eq 0 ]; then
print_message $GREEN "ruoyi-snailjob-server 编译成功"
else
print_message $RED "ruoyi-snailjob-server 编译失败"
return 1
fi
# 返回原目录
cd ../../部署文档/杭州第一技师部署文档
print_message $GREEN "所有扩展服务编译完成"
}
# 显示帮助信息
show_help() {
echo "扩展服务启动停止一体化脚本使用方法:"
echo " $0 [命令] [服务名称]"
echo ""
echo "命令:"
echo " start 启动服务"
echo " stop 停止服务"
echo " restart 重启服务"
echo " status 查看服务状态"
echo " build 编译项目"
echo ""
echo "服务名称:"
echo " monitor 启动监控中心服务 (端口: 9090)"
echo " snailjob 启动任务调度服务 (端口: 8800)"
echo " all 操作所有服务"
echo ""
echo "示例:"
echo " $0 start monitor # 启动监控中心服务"
echo " $0 start snailjob # 启动任务调度服务"
echo " $0 start all # 启动所有服务"
echo " $0 stop all # 停止所有服务"
echo " $0 status all # 查看所有服务状态"
echo " $0 build # 编译所有扩展服务"
echo ""
echo "服务访问地址:"
echo " 监控中心: http://localhost:9090/admin"
echo " 任务调度: http://localhost:8800/snail-job"
}
# 主函数
main() {
local action=$1
local service=$2
# 检查参数
if [ -z "$action" ]; then
show_help
exit 1
fi
case $action in
start)
if [ "$service" = "all" ]; then
start_all
elif [ -n "${SERVICES[$service]}" ]; then
start_service $service
else
print_message $RED "无效的服务名称: $service"
show_help
exit 1
fi
;;
stop)
if [ "$service" = "all" ]; then
stop_all
elif [ -n "${SERVICES[$service]}" ]; then
stop_service $service
else
print_message $RED "无效的服务名称: $service"
show_help
exit 1
fi
;;
restart)
if [ "$service" = "all" ]; then
restart_all
elif [ -n "${SERVICES[$service]}" ]; then
restart_service $service
else
print_message $RED "无效的服务名称: $service"
show_help
exit 1
fi
;;
status)
if [ "$service" = "all" ]; then
status_all
elif [ -n "${SERVICES[$service]}" ]; then
status_service $service
else
print_message $RED "无效的服务名称: $service"
show_help
exit 1
fi
;;
build)
build_project
;;
*)
print_message $RED "无效的命令: $action"
show_help
exit 1
;;
esac
}
# 执行主函数
main "$@"
@@ -0,0 +1,296 @@
@echo off
chcp 65001 >nul
setlocal enabledelayedexpansion
REM 多实例启动脚本 (Windows版本)
REM 使用方法: start-multi-instance.bat [start|stop|restart|status] [instance_name]
REM 实例名称: 9900, 9901, 9902, 9903, all
REM 配置变量
set JAR_NAME=ruoyi-admin.jar
set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
set JAVA_OPTS=%JAVA_OPTS% -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai
set JAVA_OPTS=%JAVA_OPTS% -XX:+UseG1GC -XX:MaxGCPauseMillis=200
set JAVA_OPTS=%JAVA_OPTS% -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs\
REM 日志目录
if not exist logs mkdir logs
REM 颜色输出函数
:print_message
set color=%1
set message=%2
if "%color%"=="RED" (
echo [31m%message%[0m
) else if "%color%"=="GREEN" (
echo [32m%message%[0m
) else if "%color%"=="YELLOW" (
echo [33m%message%[0m
) else (
echo %message%
)
goto :eof
REM 检查JAR文件是否存在
:check_jar
if not exist "%JAR_NAME%" (
call :print_message RED "错误: %JAR_NAME% 文件不存在!"
call :print_message YELLOW "请确保在正确的目录下运行此脚本,或先执行 mvn clean package -Pprod 编译项目"
pause
exit /b 1
)
goto :eof
REM 启动单个实例
:start_instance
set port=%1
set profile=%2
set pid_file=logs\ruoyi-admin-%port%.pid
set log_file=logs\ruoyi-admin-%port%.log
REM 检查是否已经运行
if exist "%pid_file%" (
for /f "tokens=*" %%i in (%pid_file%) do set pid=%%i
tasklist /FI "PID eq !pid!" 2>nul | find /I "!pid!" >nul
if !errorlevel! equ 0 (
call :print_message YELLOW "实例 %port% 已经在运行中 (PID: !pid!)"
goto :eof
) else (
REM PID文件存在但进程不存在,清理PID文件
del "%pid_file%" 2>nul
)
)
call :print_message GREEN "正在启动实例 %port% (配置文件: %profile%)..."
REM 启动应用
start /b java %JAVA_OPTS% -Dspring.profiles.active=%profile% -Dserver.port=%port% -jar %JAR_NAME% > "%log_file%" 2>&1
REM 等待启动
timeout /t 3 /nobreak >nul
REM 查找新启动的进程
for /f "tokens=2" %%i in ('tasklist /FI "IMAGENAME eq java.exe" /FO CSV ^| findstr /I "java.exe"') do (
set new_pid=%%i
set new_pid=!new_pid:"=!
echo !new_pid! > "%pid_file%"
call :print_message GREEN "实例 %port% 启动成功 (PID: !new_pid!)"
call :print_message GREEN "访问地址: http://localhost:%port%"
call :print_message GREEN "日志文件: %log_file%"
goto :eof
)
call :print_message RED "实例 %port% 启动失败,请检查日志文件: %log_file%"
if exist "%pid_file%" del "%pid_file%"
goto :eof
REM 停止单个实例
:stop_instance
set port=%1
set pid_file=logs\ruoyi-admin-%port%.pid
if exist "%pid_file%" (
for /f "tokens=*" %%i in (%pid_file%) do set pid=%%i
tasklist /FI "PID eq !pid!" 2>nul | find /I "!pid!" >nul
if !errorlevel! equ 0 (
call :print_message YELLOW "正在停止实例 %port% (PID: !pid!)..."
taskkill /PID !pid! /F >nul 2>&1
del "%pid_file%" 2>nul
call :print_message GREEN "实例 %port% 已停止"
) else (
call :print_message YELLOW "实例 %port% 未运行"
del "%pid_file%" 2>nul
)
) else (
call :print_message YELLOW "实例 %port% 未运行"
)
goto :eof
REM 重启单个实例
:restart_instance
set port=%1
call :print_message YELLOW "重启实例 %port%..."
call :stop_instance %port%
timeout /t 2 /nobreak >nul
if "%port%"=="9900" call :start_instance %port% prod-9900
if "%port%"=="9901" call :start_instance %port% prod-9901
if "%port%"=="9902" call :start_instance %port% prod-9902
if "%port%"=="9903" call :start_instance %port% prod-9903
goto :eof
REM 查看实例状态
:status_instance
set port=%1
set pid_file=logs\ruoyi-admin-%port%.pid
if exist "%pid_file%" (
for /f "tokens=*" %%i in (%pid_file%) do set pid=%%i
tasklist /FI "PID eq !pid!" 2>nul | find /I "!pid!" >nul
if !errorlevel! equ 0 (
call :print_message GREEN "实例 %port%: 运行中 (PID: !pid!)"
) else (
call :print_message RED "实例 %port%: 异常 (PID文件存在但进程不存在)"
del "%pid_file%" 2>nul
)
) else (
call :print_message YELLOW "实例 %port%: 未运行"
)
goto :eof
REM 启动所有实例
:start_all
call :print_message GREEN "启动所有实例..."
call :start_instance 9900 prod-9900
timeout /t 2 /nobreak >nul
call :start_instance 9901 prod-9901
timeout /t 2 /nobreak >nul
call :start_instance 9902 prod-9902
timeout /t 2 /nobreak >nul
call :start_instance 9903 prod-9903
goto :eof
REM 停止所有实例
:stop_all
call :print_message YELLOW "停止所有实例..."
call :stop_instance 9900
call :stop_instance 9901
call :stop_instance 9902
call :stop_instance 9903
goto :eof
REM 重启所有实例
:restart_all
call :print_message YELLOW "重启所有实例..."
call :stop_all
timeout /t 3 /nobreak >nul
call :start_all
goto :eof
REM 查看所有实例状态
:status_all
call :print_message GREEN "所有实例状态:"
call :status_instance 9900
call :status_instance 9901
call :status_instance 9902
call :status_instance 9903
goto :eof
REM 显示帮助信息
:show_help
echo 多实例启动脚本使用方法:
echo %0 [命令] [实例名称]
echo.
echo 命令:
echo start 启动实例
echo stop 停止实例
echo restart 重启实例
echo status 查看实例状态
echo.
echo 实例名称:
echo 9900 启动9900端口实例
echo 9901 启动9901端口实例
echo 9902 启动9902端口实例
echo 9903 启动9903端口实例
echo all 操作所有实例
echo.
echo 示例:
echo %0 start 9900 # 启动9900端口实例
echo %0 start all # 启动所有实例
echo %0 stop 9900 # 停止9900端口实例
echo %0 status all # 查看所有实例状态
echo %0 restart all # 重启所有实例
goto :eof
REM 主函数
:main
set action=%1
set instance=%2
REM 检查参数
if "%action%"=="" (
call :show_help
pause
exit /b 1
)
REM 检查JAR文件
call :check_jar
if !errorlevel! neq 0 goto :eof
if "%action%"=="start" (
if "%instance%"=="all" (
call :start_all
) else if "%instance%"=="9900" (
call :start_instance 9900 prod-9900
) else if "%instance%"=="9901" (
call :start_instance 9901 prod-9901
) else if "%instance%"=="9902" (
call :start_instance 9902 prod-9902
) else if "%instance%"=="9903" (
call :start_instance 9903 prod-9903
) else (
call :print_message RED "无效的实例名称: %instance%"
call :show_help
pause
exit /b 1
)
) else if "%action%"=="stop" (
if "%instance%"=="all" (
call :stop_all
) else if "%instance%"=="9900" (
call :stop_instance 9900
) else if "%instance%"=="9901" (
call :stop_instance 9901
) else if "%instance%"=="9902" (
call :stop_instance 9902
) else if "%instance%"=="9903" (
call :stop_instance 9903
) else (
call :print_message RED "无效的实例名称: %instance%"
call :show_help
pause
exit /b 1
)
) else if "%action%"=="restart" (
if "%instance%"=="all" (
call :restart_all
) else if "%instance%"=="9900" (
call :restart_instance 9900
) else if "%instance%"=="9901" (
call :restart_instance 9901
) else if "%instance%"=="9902" (
call :restart_instance 9902
) else if "%instance%"=="9903" (
call :restart_instance 9903
) else (
call :print_message RED "无效的实例名称: %instance%"
call :show_help
pause
exit /b 1
)
) else if "%action%"=="status" (
if "%instance%"=="all" (
call :status_all
) else if "%instance%"=="9900" (
call :status_instance 9900
) else if "%instance%"=="9901" (
call :status_instance 9901
) else if "%instance%"=="9902" (
call :status_instance 9902
) else if "%instance%"=="9903" (
call :status_instance 9903
) else (
call :print_message RED "无效的实例名称: %instance%"
call :show_help
pause
exit /b 1
)
) else (
call :print_message RED "无效的命令: %action%"
call :show_help
pause
exit /b 1
)
pause
goto :eof
@@ -0,0 +1,284 @@
#!/bin/bash
# 多实例启动脚本
# 使用方法: ./start-multi-instance.sh [start|stop|restart|status] [instance_name]
# 实例名称: 9900, 9901, 9902, 9903, all
# 配置变量
JAR_NAME="ruoyi-admin.jar"
JAVA_OPTS="-Xms8192m -Xmx8192m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m"
JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai"
JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/"
# 实例配置
declare -A INSTANCES
INSTANCES[9900]="prod-9900"
INSTANCES[9901]="prod-9901"
INSTANCES[9902]="prod-9902"
INSTANCES[9903]="prod-9903"
# 日志目录
LOG_DIR="logs"
mkdir -p $LOG_DIR
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 打印带颜色的消息
print_message() {
local color=$1
local message=$2
echo -e "${color}${message}${NC}"
}
# 检查JAR文件是否存在
check_jar() {
if [ ! -f "$JAR_NAME" ]; then
print_message $RED "错误: $JAR_NAME 文件不存在!"
print_message $YELLOW "请确保在正确的目录下运行此脚本,或先执行 mvn clean package -Pprod 编译项目"
exit 1
fi
}
# 启动单个实例
start_instance() {
local port=$1
local profile=$2
local pid_file="logs/ruoyi-admin-${port}.pid"
local log_file="logs/ruoyi-admin-${port}.log"
# 检查是否已经运行
if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file")
if ps -p $pid > /dev/null 2>&1; then
print_message $YELLOW "实例 $port 已经在运行中 (PID: $pid)"
return
else
# PID文件存在但进程不存在,清理PID文件
rm -f "$pid_file"
fi
fi
print_message $GREEN "正在启动实例 $port (配置文件: $profile)..."
# 启动应用
nohup java $JAVA_OPTS \
-Dspring.profiles.active=prod,$profile \
-Dserver.port=$port \
-jar $JAR_NAME \
> "$log_file" 2>&1 &
local new_pid=$!
echo $new_pid > "$pid_file"
# 等待启动
sleep 3
# 检查启动状态
if ps -p $new_pid > /dev/null 2>&1; then
print_message $GREEN "实例 $port 启动成功 (PID: $new_pid)"
print_message $GREEN "访问地址: http://localhost:$port"
print_message $GREEN "日志文件: $log_file"
else
print_message $RED "实例 $port 启动失败,请检查日志文件: $log_file"
rm -f "$pid_file"
fi
}
# 停止单个实例
stop_instance() {
local port=$1
local pid_file="logs/ruoyi-admin-${port}.pid"
if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file")
if ps -p $pid > /dev/null 2>&1; then
print_message $YELLOW "正在停止实例 $port (PID: $pid)..."
kill $pid
# 等待进程结束
local count=0
while ps -p $pid > /dev/null 2>&1 && [ $count -lt 30 ]; do
sleep 1
count=$((count + 1))
done
# 强制杀死进程
if ps -p $pid > /dev/null 2>&1; then
print_message $YELLOW "强制停止实例 $port..."
kill -9 $pid
fi
rm -f "$pid_file"
print_message $GREEN "实例 $port 已停止"
else
print_message $YELLOW "实例 $port 未运行"
rm -f "$pid_file"
fi
else
print_message $YELLOW "实例 $port 未运行"
fi
}
# 重启单个实例
restart_instance() {
local port=$1
print_message $YELLOW "重启实例 $port..."
stop_instance $port
sleep 2
start_instance $port ${INSTANCES[$port]}
}
# 查看实例状态
status_instance() {
local port=$1
local pid_file="logs/ruoyi-admin-${port}.pid"
if [ -f "$pid_file" ]; then
local pid=$(cat "$pid_file")
if ps -p $pid > /dev/null 2>&1; then
local memory=$(ps -o rss= -p $pid | awk '{print $1/1024 " MB"}')
local uptime=$(ps -o etime= -p $pid)
print_message $GREEN "实例 $port: 运行中 (PID: $pid, 内存: $memory, 运行时间: $uptime)"
else
print_message $RED "实例 $port: 异常 (PID文件存在但进程不存在)"
rm -f "$pid_file"
fi
else
print_message $YELLOW "实例 $port: 未运行"
fi
}
# 启动所有实例
start_all() {
print_message $GREEN "启动所有实例..."
for port in "${!INSTANCES[@]}"; do
start_instance $port ${INSTANCES[$port]}
sleep 2
done
}
# 停止所有实例
stop_all() {
print_message $YELLOW "停止所有实例..."
for port in "${!INSTANCES[@]}"; do
stop_instance $port
done
}
# 重启所有实例
restart_all() {
print_message $YELLOW "重启所有实例..."
stop_all
sleep 3
start_all
}
# 查看所有实例状态
status_all() {
print_message $GREEN "所有实例状态:"
for port in "${!INSTANCES[@]}"; do
status_instance $port
done
}
# 显示帮助信息
show_help() {
echo "多实例启动脚本使用方法:"
echo " $0 [命令] [实例名称]"
echo ""
echo "命令:"
echo " start 启动实例"
echo " stop 停止实例"
echo " restart 重启实例"
echo " status 查看实例状态"
echo ""
echo "实例名称:"
echo " 9900 启动9900端口实例"
echo " 9901 启动9901端口实例"
echo " 9902 启动9902端口实例"
echo " 9903 启动9903端口实例"
echo " all 操作所有实例"
echo ""
echo "示例:"
echo " $0 start 9900 # 启动9900端口实例"
echo " $0 start all # 启动所有实例"
echo " $0 stop 9900 # 停止9900端口实例"
echo " $0 status all # 查看所有实例状态"
echo " $0 restart all # 重启所有实例"
}
# 主函数
main() {
local action=$1
local instance=$2
# 检查参数
if [ -z "$action" ]; then
show_help
exit 1
fi
# 检查JAR文件
check_jar
case $action in
start)
if [ "$instance" = "all" ]; then
start_all
elif [ -n "${INSTANCES[$instance]}" ]; then
start_instance $instance ${INSTANCES[$instance]}
else
print_message $RED "无效的实例名称: $instance"
show_help
exit 1
fi
;;
stop)
if [ "$instance" = "all" ]; then
stop_all
elif [ -n "${INSTANCES[$instance]}" ]; then
stop_instance $instance
else
print_message $RED "无效的实例名称: $instance"
show_help
exit 1
fi
;;
restart)
if [ "$instance" = "all" ]; then
restart_all
elif [ -n "${INSTANCES[$instance]}" ]; then
restart_instance $instance
else
print_message $RED "无效的实例名称: $instance"
show_help
exit 1
fi
;;
status)
if [ "$instance" = "all" ]; then
status_all
elif [ -n "${INSTANCES[$instance]}" ]; then
status_instance $instance
else
print_message $RED "无效的实例名称: $instance"
show_help
exit 1
fi
;;
*)
print_message $RED "无效的命令: $action"
show_help
exit 1
;;
esac
}
# 执行主函数
main "$@"
@@ -0,0 +1,66 @@
#!/bin/bash
# 配置修复验证脚本
# 使用方法: ./test-config-fix.sh
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
print_message() {
local color=$1
local message=$2
echo -e "${color}${message}${NC}"
}
print_message $GREEN "=== 多实例配置修复验证脚本 ==="
# 检查JAR文件
JAR_NAME="ruoyi-admin.jar"
if [ ! -f "$JAR_NAME" ]; then
print_message $RED "错误: $JAR_NAME 文件不存在!"
print_message $YELLOW "请先执行 mvn clean package -Pprod 编译项目"
exit 1
fi
print_message $GREEN "✓ JAR文件存在"
# 检查配置文件
CONFIG_FILES=(
"backend/ruoyi-admin/src/main/resources/application-prod-9900.yml"
"backend/ruoyi-admin/src/main/resources/application-prod-9901.yml"
"backend/ruoyi-admin/src/main/resources/application-prod-9902.yml"
"backend/ruoyi-admin/src/main/resources/application-prod-9903.yml"
)
for config_file in "${CONFIG_FILES[@]}"; do
if [ -f "$config_file" ]; then
# 检查是否包含spring.profiles.include
if grep -q "spring.profiles.include" "$config_file"; then
print_message $RED "✗ $config_file 仍包含 spring.profiles.include"
else
print_message $GREEN "✓ $config_file 配置正确"
fi
else
print_message $RED "✗ $config_file 文件不存在"
fi
done
# 检查启动脚本
START_SCRIPT="docs/部署文档/杭州第一技师部署文档/start-multi-instance.sh"
if [ -f "$START_SCRIPT" ]; then
if grep -q "spring.profiles.active=prod,\$profile" "$START_SCRIPT"; then
print_message $GREEN "✓ 启动脚本配置正确"
else
print_message $RED "✗ 启动脚本配置错误"
fi
else
print_message $RED "✗ 启动脚本不存在"
fi
print_message $GREEN ""
print_message $GREEN "=== 验证完成 ==="
print_message $YELLOW "如果所有检查都通过,可以尝试启动实例:"
print_message $YELLOW " ./start-multi-instance.sh start 9900"
@@ -0,0 +1,96 @@
# 多实例启动配置修复说明
## 问题描述
在使用 `start-multi-instance.sh` 启动实例时,出现以下错误:
```
org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property 'spring.profiles.include' imported from location 'class path resource [application-prod-9900.yml]' is invalid in a profile specific resource
```
## 问题原因
在Spring Boot 2.4+版本中,`spring.profiles.include` 属性不能在profile-specific的配置文件中使用(如 `application-prod-9900.yml`)。这个属性只能在主配置文件(`application.yml`)中使用。
## 修复方案
### 1. 修改配置文件结构
移除了所有实例配置文件中的 `spring.profiles.include: prod` 配置:
**修复前:**
```yaml
# application-prod-9900.yml
spring:
profiles:
include: prod # ❌ 这在profile-specific文件中不被允许
```
**修复后:**
```yaml
# application-prod-9900.yml
spring:
application:
name: RuoYi-Vue-Plus-9900 # ✅ 直接配置应用名称
```
### 2. 修改启动脚本
修改启动脚本,在启动时同时激活 `prod` 和具体的实例profile:
**修复前:**
```bash
-Dspring.profiles.active=$profile # 只激活 prod-9900
```
**修复后:**
```bash
-Dspring.profiles.active=prod,$profile # 同时激活 prod 和 prod-9900
```
## 修复的文件
1. `backend/ruoyi-admin/src/main/resources/application-prod-9900.yml`
2. `backend/ruoyi-admin/src/main/resources/application-prod-9901.yml`
3. `backend/ruoyi-admin/src/main/resources/application-prod-9902.yml`
4. `backend/ruoyi-admin/src/main/resources/application-prod-9903.yml`
5. `docs/部署文档/杭州第一技师部署文档/start-multi-instance.sh`
## 配置加载顺序
修复后的配置加载顺序:
1. `application.yml` (主配置文件)
2. `application-prod.yml` (生产环境基础配置)
3. `application-prod-9900.yml` (9900端口实例特定配置)
## 验证方法
1. 重新编译项目:
```bash
mvn clean package -Pprod
```
2. 启动单个实例测试:
```bash
./start-multi-instance.sh start 9900
```
3. 检查启动日志,确认没有配置错误
4. 访问应用验证功能正常:
```bash
curl http://localhost:9900
```
## 注意事项
1. 确保在修改配置文件后重新编译项目
2. 如果之前有实例在运行,需要先停止再重新启动
3. 检查日志文件确认配置加载正确
4. 验证监控中心和SnailJob配置是否正常工作
## 相关文档
- [Spring Boot Profile配置最佳实践](https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.files.profile-specific)
- [多实例部署指南](../README.md)
@@ -0,0 +1,293 @@
# 🚀 RuoYi-Admin 多实例部署说明
## 📋 概述
本文档说明如何在生产环境中部署多个 `ruoyi-admin.jar` 实例到不同端口,实现负载均衡和高可用。
## 🏗️ 部署架构
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 前端应用 │ │ Nginx │ │ 后端实例 │
│ │ │ 负载均衡器 │ │ │
│ http://80 │───▶│ http://80 │───▶│ 9900, 9901 │
│ │ │ │ │ 9902, 9903 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
## 📁 文件结构
```
backend/
├── ruoyi-admin.jar # 编译后的JAR包
├── application-prod-9900.yml # 9900端口配置文件
├── application-prod-9901.yml # 9901端口配置文件
├── application-prod-9902.yml # 9902端口配置文件
├── application-prod-9903.yml # 9903端口配置文件
├── start-multi-instance.sh # Linux启动脚本
├── start-multi-instance.bat # Windows启动脚本
├── docker-compose-multi.yml # Docker部署配置
├── nginx.conf # Nginx负载均衡配置
└── logs/ # 日志目录
├── ruoyi-admin-9900.log
├── ruoyi-admin-9901.log
├── ruoyi-admin-9902.log
└── ruoyi-admin-9903.log
```
## 🔧 部署步骤
### 1. 编译项目
```bash
# 进入backend目录
cd backend
# 编译生产环境JAR包
mvn clean package -Pprod
# 检查生成的JAR文件
ls -la ruoyi-admin/target/ruoyi-admin.jar
```
### 2. 准备部署文件
```bash
# 复制JAR文件到部署目录
cp ruoyi-admin/target/ruoyi-admin.jar ./
# 创建日志目录
mkdir -p logs
# 设置脚本执行权限 (Linux)
chmod +x start-multi-instance.sh
```
### 3. 启动多实例
#### Linux环境
```bash
# 启动所有实例
./start-multi-instance.sh start all
# 启动单个实例
./start-multi-instance.sh start 9900
./start-multi-instance.sh start 9901
./start-multi-instance.sh start 9902
./start-multi-instance.sh start 9903
# 查看实例状态
./start-multi-instance.sh status all
# 停止实例
./start-multi-instance.sh stop all
# 重启实例
./start-multi-instance.sh restart all
```
#### Windows环境
```cmd
# 启动所有实例
start-multi-instance.bat start all
# 启动单个实例
start-multi-instance.bat start 9900
start-multi-instance.bat start 9901
start-multi-instance.bat start 9902
start-multi-instance.bat start 9903
# 查看实例状态
start-multi-instance.bat status all
# 停止实例
start-multi-instance.bat stop all
# 重启实例
start-multi-instance.bat restart all
```
### 4. Docker部署 (可选)
```bash
# 启动所有服务
docker-compose -f docker-compose-multi.yml up -d
# 查看服务状态
docker-compose -f docker-compose-multi.yml ps
# 查看日志
docker-compose -f docker-compose-multi.yml logs -f
# 停止服务
docker-compose -f docker-compose-multi.yml down
```
## 🌐 访问地址
### 直接访问各实例
- **实例1**: http://localhost:9900
- **实例2**: http://localhost:9901
- **实例3**: http://localhost:9902
- **实例4**: http://localhost:9903
### 通过负载均衡器访问
- **主入口**: http://localhost (Nginx负载均衡)
- **监控页面**: http://localhost:8080/status
## 📊 监控和管理
### 1. 实例状态监控
```bash
# 查看所有实例状态
./start-multi-instance.sh status all
# 查看单个实例状态
./start-multi-instance.sh status 9900
```
### 2. 日志监控
```bash
# 查看实时日志
tail -f logs/ruoyi-admin-9900.log
tail -f logs/ruoyi-admin-9901.log
tail -f logs/ruoyi-admin-9902.log
tail -f logs/ruoyi-admin-9903.log
# 查看错误日志
grep "ERROR" logs/ruoyi-admin-*.log
```
### 3. 进程监控
```bash
# 查看Java进程
ps aux | grep java
# 查看端口占用
netstat -tlnp | grep :99
```
## ⚠️ 注意事项
### 1. 端口冲突
- 确保9900-9903端口未被其他服务占用
- 检查防火墙设置,确保端口可访问
### 2. 内存配置
- 每个实例默认配置:最小512MB,最大1024MB
- 根据服务器配置调整 `JAVA_OPTS` 参数
### 3. 数据库连接
- 所有实例共享同一个数据库
- 确保数据库连接池配置合理,避免连接数过多
### 4. 文件上传
- 临时文件目录:`/ruoyi/server/temp`
- 确保目录存在且有写权限
## 🔄 故障排查
### 1. 实例启动失败
```bash
# 检查JAR文件是否存在
ls -la ruoyi-admin.jar
# 检查端口是否被占用
netstat -tlnp | grep :99
# 查看启动日志
tail -f logs/ruoyi-admin-9900.log
```
### 2. 实例无法访问
```bash
# 检查防火墙设置
firewall-cmd --list-ports
# 检查SELinux状态 (CentOS/RHEL)
getenforce
# 测试端口连通性
telnet localhost 9900
```
### 3. 负载均衡不工作
```bash
# 检查Nginx状态
docker-compose -f docker-compose-multi.yml ps nginx
# 查看Nginx日志
docker-compose -f docker-compose-multi.yml logs nginx
# 测试后端服务
curl http://localhost:9900/health
```
## 📈 性能优化建议
### 1. JVM调优
```bash
# 根据服务器内存调整JVM参数
JAVA_OPTS="-Xms2g -Xmx4g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
```
### 2. 数据库连接池优化
```yaml
# 在application-prod-*.yml中调整
spring:
datasource:
hikari:
maxPoolSize: 100 # 根据实例数调整
minIdle: 20
connectionTimeout: 30000
```
### 3. 缓存配置
```yaml
# Redis连接池优化
redisson:
threads: 32
nettyThreads: 64
singleServerConfig:
connectionPoolSize: 128
```
## 🚀 扩展部署
### 1. 添加更多实例
- 复制配置文件模板
- 修改端口号和应用名称
- 更新启动脚本和Docker配置
### 2. 集群部署
- 使用Redis集群
- 配置数据库主从复制
- 实现会话共享
### 3. 容器化部署
- 使用Kubernetes管理
- 配置自动扩缩容
- 实现滚动更新
## 📞 技术支持
如遇到部署问题,请检查:
1. 系统日志和应用日志
2. 端口占用情况
3. 防火墙和网络配置
4. 数据库连接状态
5. 内存和磁盘空间
---
**📝 文档版本**: v1.0
**🔄 最后更新**: 2025-01-03
**👨‍💻 维护人员**: 开发团队