前端-胡立永 4 weeks ago
parent
commit
234041fe53
2 changed files with 0 additions and 1256 deletions
  1. +0
    -266
      README-restart-script.md
  2. +0
    -990
      restart-java-app.sh

+ 0
- 266
README-restart-script.md View File

@ -1,266 +0,0 @@
# Java应用启动脚本使用说明
## 概述
`restart-java-app.sh` 是一个功能强大的Java应用管理脚本,支持启动、停止、重启和状态查看等操作。脚本支持单个JAR包操作和多JAR包批量操作,提供灵活的JAR包昵称匹配功能。
## 主要功能
- ✅ **自动JAR包检测** - 当目录中只有一个JAR包时自动选择
- ✅ **多JAR包选择** - 当目录中有多个JAR包时提供交互式选择菜单
- ✅ **多JAR包批量操作** - 支持通过昵称同时操作多个JAR包
- ✅ **模糊匹配** - 支持通过昵称模糊匹配JAR文件名
- ✅ **全量操作** - 支持使用'all'参数操作所有JAR包
- ✅ **进程管理** - 自动管理应用进程的启动和停止
- ✅ **日志管理** - 自动生成基于JAR包名和时间戳的日志文件
- ✅ **状态监控** - 实时查看应用运行状态
- ✅ **优雅停止** - 先尝试正常停止,必要时强制终止
## 配置参数
在使用脚本前,需要修改脚本开头的配置参数:
```bash
# ========================================
# 配置参数 - 请根据实际情况修改
# ========================================
JAR_NAME="" # JAR包名称(留空则自动检测)
```
### 主要配置项
| 参数 | 说明 | 示例 |
|------|------|------|
| `JAR_NAME` | JAR包文件名(留空可自动检测) | `"my-spring-boot-app.jar"``""` |
**自动检测功能**: 如果将 `JAR_NAME` 留空(`JAR_NAME=""`),脚本会自动检测当前目录下的JAR文件:
- 如果只有一个JAR文件,自动使用该文件
- 如果有多个JAR文件,会显示选择菜单让用户选择具体的JAR文件
- 如果没有JAR文件,会提示错误
### 可选配置项
| 参数 | 说明 | 默认值 |
|------|------|--------|
| `JAVA_OPTS` | JVM启动参数 | `""` (空值,使用JVM默认设置,节省内存) |
| `LOG_FILE` | 日志文件名(留空则自动生成) | `""` |
| `PID_FILE` | PID文件名 | `"app.pid"` |
## 使用方法
### 1. 设置执行权限
```bash
chmod +x restart-java-app.sh
```
### 2. 基本用法
```bash
# 重启应用(默认操作)
./restart-java-app.sh
# 或者明确指定重启
./restart-java-app.sh restart
```
### 3. 其他命令
```bash
# 启动应用
./restart-java-app.sh start
# 停止应用
./restart-java-app.sh stop
# 查看应用状态
./restart-java-app.sh status
# 显示帮助信息
./restart-java-app.sh help
```
## 命令详解
### start - 启动应用
- 检查JAR文件是否存在
- 启动Java应用
- 等待应用完全启动
- 验证应用启动状态
### stop - 停止应用
- 查找应用进程PID
- 发送TERM信号优雅停止
- 等待最多30秒
- 超时后强制停止(KILL信号)
- 清理PID文件
### restart - 重启应用
- 先执行stop操作
- 等待2秒
- 再执行start操作
### status - 查看状态
- 显示应用运行状态
- 显示进程PID
## 输出说明
脚本使用彩色输出来区分不同类型的消息:
- 🔴 **红色**: 错误信息
- 🟢 **绿色**: 成功信息
- 🟡 **黄色**: 警告信息
- 🔵 **蓝色**: 操作信息
## 文件说明
脚本运行时会创建以下文件:
- **日志文件**: 自动生成,存储在 `log/{JAR包名}/` 目录下,格式为 `{JAR包名}_{启动时间}.log`
- **PID文件**: `app.pid` - 应用进程PID文件
### 日志文件命名规则
- **自动生成模式**(推荐):当 `LOG_FILE=""` 时,系统会自动生成日志文件名和路径
- **目录结构**: `log/{JAR包名称}/`
- **文件格式**: `{JAR包名称}_{年月日_时分秒}.log`
- **示例**:
- JAR包:`my-spring-boot-app.jar`
- 启动时间:2024年1月15日 14:30:25
- 日志文件路径:`log/my-spring-boot-app/my-spring-boot-app_20240115_143025.log`
- **目录自动创建**:脚本会自动创建 `log/{JAR包名}/` 目录结构
- **手动指定**:如果在脚本中设置了 `LOG_FILE="custom.log"`,则使用指定的文件名
## 常见问题
### Q: 提示"JAR文件不存在"
**A**:
- 如果使用自动检测模式(`JAR_NAME=""`),确保当前目录下有且仅有一个JAR文件
- 如果指定了JAR文件名,检查`JAR_NAME`配置是否正确,确保JAR文件在脚本同一目录下
### Q: 提示"当前目录下有多个JAR文件"
**A**: 当目录下有多个JAR文件时,脚本会显示选择菜单,按提示输入对应的序号即可选择要启动的JAR文件。你也可以在脚本中明确指定`JAR_NAME`,例如:`JAR_NAME="my-app.jar"`
### Q: 应用启动失败
**A**: 查看`app.log`日志文件,检查具体错误原因。
### Q: 无法停止应用
**A**: 脚本会先尝试优雅停止,30秒后自动强制停止。
## 系统要求
- Linux操作系统
- Bash Shell
- Java运行环境
## 注意事项
1. 确保脚本有执行权限
2. 确保Java环境已正确安装
3. 确保JAR文件路径正确
4. 建议在生产环境使用前先在测试环境验证
5. 定期检查日志文件大小,避免占用过多磁盘空间
## 示例
### 示例1: 自动检测模式(推荐)
如果你的目录下只有一个JAR文件,可以使用自动检测模式:
1. 保持脚本配置为默认:
```bash
JAR_NAME="" # 留空,自动检测
```
2. 启动应用:
```bash
# 启动应用
./restart-java-app.sh start
# 重启应用
./restart-java-app.sh restart
```
脚本会自动检测并使用目录下唯一的JAR文件。
### 示例2: 指定JAR文件名
假设你有一个名为`my-app.jar`的Spring Boot应用:
1. 修改脚本配置:
```bash
JAR_NAME="my-app.jar"
```
2. 使用不同方式启动:
```bash
# 启动应用
./restart-java-app.sh start
# 重启应用
./restart-java-app.sh restart
# 停止应用
./restart-java-app.sh stop
```
3. 查看状态:
```bash
./restart-java-app.sh status
```
### 示例3: 多JAR文件选择
当目录下有多个JAR文件时,脚本会提供交互式选择:
```bash
# 启动脚本,会显示选择菜单
./restart-java-app.sh start
```
## 交互式JAR包选择
当脚本检测到多个JAR文件时,会提供交互式选择界面:
```bash
检测到多个JAR文件,请选择要启动的应用:
[1] user-service-1.0.0.jar
[2] order-service-1.0.0.jar
[3] payment-service-1.0.0.jar
输入选项:
- 单个序号: 1 (选择第1个JAR包)
- 多个序号: 1,3,5 或 1 3 5 (选择多个JAR包)
- 全部选择: all (选择所有JAR包)
请输入选择:
```
**支持的输入格式:**
- `1` - 选择单个JAR包
- `1,3,5` - 选择多个JAR包(逗号分隔)
- `1 3 5` - 选择多个JAR包(空格分隔)
- `all` - 选择所有JAR包
选择多个JAR包时,脚本会自动切换到批量操作模式。
输出示例:
```
检测到多个JAR文件,请选择要启动的应用:
[1] my-web-app.jar
[2] my-api-service.jar
[3] my-batch-job.jar
请输入序号 (1-3): 2
✓ 已选择: my-api-service.jar
正在启动应用...
JAR包: my-api-service.jar
JVM参数: (使用默认设置)
日志文件: log/my-api-service/my-api-service_20240115_143025.log
✓ 应用启动成功 (PID: 12345)
日志文件: log/my-api-service/my-api-service_20240115_143025.log
```

+ 0
- 990
restart-java-app.sh View File

@ -1,990 +0,0 @@
#!/bin/bash
# ========================================
# Java应用启动/重启脚本
# 使用方法: ./restart-java-app.sh
# ========================================
# 全局配置
JAR_NAME="" # JAR文件名(为空时自动检测)
JAVA_OPTS="" # JVM参数(为空时使用默认值,节省内存)
LOG_FILE="" # 日志文件名(自动生成)
# 运行时配置常量
STOP_TIMEOUT=15 # 停止超时时间(秒)
STARTUP_WAIT=3 # 启动等待时间(秒)
FORCE_KILL_WAIT=2 # 强制终止等待时间(秒)
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 全局变量(用于交互式选择和批量操作)
SELECTED_JARS=() # 用户选择的JAR包数组
MULTI_JAR_MODE=false # 是否为多JAR包模式
PARSED_ACTION="" # 解析出的操作命令
PARSED_JARS=() # 解析出的JAR包列表
# ========================================
# 工具函数
# ========================================
# 打印错误消息
print_error() {
echo -e "${RED}错误: $1${NC}" >&2
}
# 打印警告消息
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
# 打印成功消息
print_success() {
echo -e "${GREEN}$1${NC}"
}
# 打印信息消息
print_info() {
echo -e "${BLUE}$1${NC}"
}
# 验证Java环境的详细检查
validate_java_environment() {
# 检查Java是否安装
if ! command -v java >/dev/null 2>&1; then
print_error "Java未安装或不在PATH中"
print_info "请安装Java运行环境(JRE)或Java开发工具包(JDK)"
return 1
fi
# 检查Java版本
local java_version
java_version=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2)
if [ -z "$java_version" ]; then
print_error "无法获取Java版本信息"
return 1
fi
print_info "检测到Java版本: $java_version"
# 检查Java是否能正常运行
if ! java -version >/dev/null 2>&1; then
print_error "Java环境异常,无法正常运行"
return 1
fi
return 0
}
# 检查磁盘空间
check_disk_space() {
local required_space_mb="${1:-100}" # 默认需要100MB空间
# 获取当前目录的可用空间(MB)
local available_space
if command -v df >/dev/null 2>&1; then
available_space=$(df . | awk 'NR==2 {print int($4/1024)}')
if [ "$available_space" -lt "$required_space_mb" ]; then
print_warning "磁盘空间不足,可用空间: ${available_space}MB,建议至少: ${required_space_mb}MB"
return 1
fi
else
print_warning "无法检查磁盘空间(df命令不可用)"
fi
return 0
}
# 检查文件权限
check_file_permissions() {
local file_path="$1"
# 检查文件是否存在
if [ ! -f "$file_path" ]; then
print_error "文件不存在: $file_path"
return 1
fi
# 检查文件是否可读
if [ ! -r "$file_path" ]; then
print_error "文件不可读: $file_path"
return 1
fi
return 0
}
# 验证JAR文件名的安全性
validate_jar_name() {
local jar_name="$1"
# 检查是否为空
if [ -z "$jar_name" ]; then
print_error "JAR文件名不能为空"
return 1
fi
# 检查文件名长度(防止过长的文件名)
if [ ${#jar_name} -gt 255 ]; then
print_error "JAR文件名过长(超过255个字符)"
return 1
fi
# 检查是否包含危险字符(路径遍历攻击)
if [[ "$jar_name" == *".."* ]] || [[ "$jar_name" == *"/"* ]] || [[ "$jar_name" == *"\\"* ]]; then
print_error "JAR文件名包含非法字符(不允许路径分隔符或相对路径)"
return 1
fi
# 检查是否以.jar结尾
if [[ ! "$jar_name" == *.jar ]]; then
print_error "文件必须是.jar格式"
return 1
fi
# 检查文件名是否包含特殊字符(可能导致命令注入)
if [[ "$jar_name" =~ [\;\|\&\$\`\<\>\(\)\{\}\[\]\"\'*\?\~] ]]; then
print_error "JAR文件名包含特殊字符,可能存在安全风险"
return 1
fi
return 0
}
# 验证用户输入的安全性
validate_user_input() {
local input="$1"
local input_type="${2:-general}"
# 检查输入长度
if [ ${#input} -gt 1000 ]; then
print_error "输入内容过长"
return 1
fi
# 根据输入类型进行不同的验证
case "$input_type" in
"number")
# 验证数字输入
if [[ ! "$input" =~ ^[0-9,[:space:]]+$ ]] && [ "$input" != "all" ]; then
print_error "输入格式错误,只允许数字、逗号、空格或'all'"
return 1
fi
;;
"action")
# 验证操作命令
if [[ ! "$input" =~ ^(start|stop|restart|status|help|-h|--help)$ ]]; then
print_error "无效的操作命令: $input"
return 1
fi
;;
"general")
# 通用输入验证,检查危险字符
if [[ "$input" =~ [\;\|\&\$\`\<\>\(\)\{\}*\?\~] ]]; then
print_error "输入包含特殊字符,可能存在安全风险"
return 1
fi
;;
esac
return 0
}
# 检查Java环境
check_java_environment() {
if ! command -v java &> /dev/null; then
print_error "Java未安装或不在PATH中"
return 1
fi
# 使用更详细的Java环境验证
if ! validate_java_environment; then
return 1
fi
# 检查磁盘空间
if ! check_disk_space; then
print_warning "磁盘空间不足可能影响应用运行"
fi
return 0
}
# 设置JAR包相关变量
setup_jar_variables() {
local jar_name="$1"
if [ -z "$jar_name" ]; then
jar_name="$JAR_NAME"
fi
# 设置日志文件名(添加时间戳)
local jar_basename=$(basename "$jar_name" .jar)
local timestamp=$(date +"%Y%m%d_%H%M%S")
# 创建日志目录结构:log/jar包名称/
local log_dir="log/${jar_basename}"
# 确保日志目录存在
if [ ! -d "$log_dir" ]; then
mkdir -p "$log_dir" 2>/dev/null
if [ $? -ne 0 ]; then
print_warning "无法创建日志目录 $log_dir,使用当前目录"
LOG_FILE="${jar_basename}_${timestamp}.log"
else
LOG_FILE="${log_dir}/${jar_basename}_${timestamp}.log"
fi
else
LOG_FILE="${log_dir}/${jar_basename}_${timestamp}.log"
fi
}
# 等待进程停止
wait_for_process_stop() {
local pid="$1"
local timeout="${2:-$STOP_TIMEOUT}"
local count=0
while [ $count -lt $timeout ]; do
if ! ps -p $pid > /dev/null 2>&1; then
return 0
fi
sleep 1
count=$((count + 1))
done
return 1
}
# 根据昵称匹配JAR文件
find_jar_by_nickname() {
local nickname="$1"
local found_jars=()
# 如果昵称是 "all",返回所有JAR文件
if [ "$nickname" = "all" ]; then
for jar in *.jar; do
if [ -f "$jar" ]; then
found_jars+=("$jar")
fi
done
else
# 模糊匹配JAR文件名
for jar in *.jar; do
if [ -f "$jar" ] && [[ "$jar" == *"$nickname"* ]]; then
found_jars+=("$jar")
fi
done
fi
# 输出匹配的JAR文件
printf '%s\n' "${found_jars[@]}"
}
# 解析JAR包参数列表
parse_jar_arguments() {
local args=("$@")
local jar_list=()
local action=""
# 如果没有参数,使用默认行为
if [ ${#args[@]} -eq 0 ]; then
action="restart"
PARSED_ACTION="$action"
return
fi
# 检查第一个参数是否是操作命令
case "${args[0]}" in
"start"|"stop"|"restart"|"status"|"help"|"-h"|"--help")
action="${args[0]}"
# 验证操作命令
if ! validate_user_input "$action" "action"; then
return 1
fi
# 如果只有操作命令,使用原有逻辑
if [ ${#args[@]} -eq 1 ]; then
PARSED_ACTION="$action"
return
fi
# 其余参数作为JAR包昵称
for ((i=1; i<${#args[@]}; i++)); do
# 验证JAR包昵称输入
if ! validate_user_input "${args[i]}" "general"; then
return 1
fi
jar_list+=("${args[i]}")
done
;;
*)
# 第一个参数不是操作命令,默认为restart操作
action="restart"
# 所有参数都作为JAR包昵称
for arg in "${args[@]}"; do
# 验证JAR包昵称输入
if ! validate_user_input "$arg" "general"; then
return 1
fi
jar_list+=("$arg")
done
;;
esac
# 设置全局变量
PARSED_ACTION="$action"
PARSED_JARS=("${jar_list[@]}")
}
# 检查JAR文件是否存在
check_jar_exists() {
# 如果JAR_NAME为空,自动检测当前目录下的JAR文件
if [ -z "$JAR_NAME" ]; then
local jar_files=(*.jar)
# 检查是否存在JAR文件
if [ ! -e "${jar_files[0]}" ]; then
print_error "当前目录下没有找到JAR文件!"
print_warning "请将JAR文件放在脚本同一目录下,或在脚本中指定JAR_NAME"
return 1
fi
# 如果只有一个JAR文件,自动使用
if [ ${#jar_files[@]} -eq 1 ]; then
JAR_NAME="${jar_files[0]}"
# 验证JAR文件名的安全性
if ! validate_jar_name "$JAR_NAME"; then
return 1
fi
print_info "自动检测到JAR文件: $JAR_NAME"
else
# 多个JAR文件时,让用户选择
print_info "检测到多个JAR文件,请选择要启动的应用:"
echo ""
# 显示选项列表
local i=1
for jar in "${jar_files[@]}"; do
echo -e "${GREEN}[$i]${NC} $jar"
((i++))
done
echo ""
# 提示用户输入
echo -e "${YELLOW}输入选项:${NC}"
echo -e " - 单个序号: ${GREEN}1${NC} (选择第1个JAR包)"
echo -e " - 多个序号: ${GREEN}1,3,5${NC}${GREEN}1 3 5${NC} (选择多个JAR包)"
echo -e " - 全部选择: ${GREEN}all${NC} (选择所有JAR包)"
echo ""
echo -n -e "${YELLOW}请输入选择: ${NC}"
read -r choice
# 验证用户输入
if ! validate_user_input "$choice" "number"; then
return 1
fi
# 处理输入
local selected_jars=()
if [ "$choice" = "all" ]; then
# 选择所有JAR包
selected_jars=("${jar_files[@]}")
print_success "已选择所有JAR包 (${#selected_jars[@]}个)"
else
# 解析序号输入(支持逗号分隔和空格分隔)
local numbers
if [[ "$choice" == *","* ]]; then
# 逗号分隔
IFS=',' read -ra numbers <<< "$choice"
else
# 空格分隔
read -ra numbers <<< "$choice"
fi
# 验证并收集选中的JAR包
local valid_selection=true
for num in "${numbers[@]}"; do
# 去除空格
num=$(echo "$num" | tr -d ' ')
if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#jar_files[@]} ]; then
selected_jars+=("${jar_files[$((num-1))]}")
else
print_error "无效的序号 '$num'!请输入1到${#jar_files[@]}之间的数字"
valid_selection=false
break
fi
done
if [ "$valid_selection" = false ]; then
return 1
fi
# 去重
selected_jars=($(printf '%s\n' "${selected_jars[@]}" | sort -u))
print_success "已选择 ${#selected_jars[@]} 个JAR包:"
for jar in "${selected_jars[@]}"; do
echo -e " - $jar"
done
fi
# 验证所有选中的JAR文件名
for jar in "${selected_jars[@]}"; do
if ! validate_jar_name "$jar"; then
return 1
fi
done
echo ""
# 如果选择了多个JAR包,设置全局变量供批量操作使用
if [ ${#selected_jars[@]} -gt 1 ]; then
SELECTED_JARS=("${selected_jars[@]}")
MULTI_JAR_MODE=true
# 设置第一个JAR作为默认JAR_NAME(兼容性)
JAR_NAME="${selected_jars[0]}"
else
# 单个JAR包
JAR_NAME="${selected_jars[0]}"
MULTI_JAR_MODE=false
fi
fi
fi
# 检查指定的JAR文件是否存在
if [ ! -f "$JAR_NAME" ]; then
print_error "JAR文件 '$JAR_NAME' 不存在!"
print_warning "请检查JAR_NAME配置是否正确,或确保JAR文件在当前目录下"
return 1
fi
# 检查文件权限
if ! check_file_permissions "$JAR_NAME"; then
return 1
fi
return 0
}
# 获取运行中的应用PID
get_app_pid() {
local jar_name="$1"
if [ -z "$jar_name" ]; then
jar_name="$JAR_NAME"
fi
# 通过进程名查找(更可靠的方法)
pgrep -f "$jar_name" | head -1
}
# 检查应用是否正在运行
is_running() {
local jar_name="$1"
local pid=$(get_app_pid "$jar_name")
if [ -n "$pid" ]; then
return 0 # 正在运行
else
return 1 # 未运行
fi
}
# 启动应用
start_app() {
local jar_name="$1"
if [ -z "$jar_name" ]; then
jar_name="$JAR_NAME"
fi
if ! check_java_environment; then
return 1
fi
setup_jar_variables "$jar_name"
if is_running "$jar_name"; then
print_warning "应用 $jar_name 已在运行中 (PID: $(get_app_pid "$jar_name"))"
return 0
fi
print_info "正在启动应用: $jar_name"
print_info "日志文件: $LOG_FILE"
# 启动应用
nohup java $JAVA_OPTS -jar "$jar_name" > "$LOG_FILE" 2>&1 &
# 等待启动
sleep "$STARTUP_WAIT"
# 验证启动状态
if is_running "$jar_name"; then
local actual_pid=$(get_app_pid "$jar_name")
print_success "应用 $jar_name 启动成功 (PID: $actual_pid)"
print_info "查看日志: tail -f $LOG_FILE"
return 0
else
print_error "应用 $jar_name 启动失败"
print_warning "请检查日志文件: $LOG_FILE"
return 1
fi
}
# 停止应用
stop_app() {
local jar_name="$1"
if [ -z "$jar_name" ]; then
jar_name="$JAR_NAME"
fi
local pid=$(get_app_pid "$jar_name")
if [ -z "$pid" ]; then
print_warning "应用 $jar_name 未运行"
return 0
fi
setup_jar_variables "$jar_name"
print_info "正在停止应用 $jar_name (PID: $pid)..."
# 发送TERM信号
kill "$pid" 2>/dev/null
# 等待进程停止
if wait_for_process_stop "$pid"; then
print_success "应用 $jar_name 已成功停止"
return 0
else
print_warning "应用 $jar_name 未在 ${STOP_TIMEOUT} 秒内停止,强制终止..."
kill -9 "$pid" 2>/dev/null
sleep "$FORCE_KILL_WAIT"
# 再次检查
if [ -z "$(get_app_pid "$jar_name")" ]; then
print_success "应用 $jar_name 已强制停止"
return 0
else
print_error "无法停止应用 $jar_name"
return 1
fi
fi
}
# 重启应用
restart_app() {
local jar_name="$1"
if [ -z "$jar_name" ]; then
jar_name="$JAR_NAME"
fi
if ! check_java_environment; then
return 1
fi
setup_jar_variables "$jar_name"
print_info "正在重启应用: $jar_name"
if is_running "$jar_name"; then
print_info "正在停止应用..."
stop_app "$jar_name"
else
print_info "应用未运行,直接启动"
fi
print_info "正在启动应用..."
start_app "$jar_name"
}
# 显示应用状态
show_status() {
local jar_name="$1"
if [ -z "$jar_name" ]; then
jar_name="$JAR_NAME"
fi
setup_jar_variables "$jar_name"
print_info "=== 应用状态 ==="
echo "JAR包: $jar_name"
echo "JVM参数: $JAVA_OPTS"
echo "日志文件: $LOG_FILE"
echo ""
local pid=$(get_app_pid "$jar_name")
if [ -n "$pid" ]; then
print_success "✓ 应用正在运行 (PID: $pid)"
# 显示进程信息
if command -v ps >/dev/null 2>&1; then
echo ""
echo "进程详情:"
ps -p "$pid" -o pid,ppid,cmd --no-headers 2>/dev/null || echo " 无法获取进程详情"
fi
else
print_warning "✗ 应用未运行"
fi
}
# 批量启动应用
batch_start_app() {
local jar_files=("$@")
local success_count=0
local total_count=${#jar_files[@]}
if [ $total_count -eq 0 ]; then
print_error "没有指定要启动的JAR包"
return 1
fi
if ! check_java_environment; then
return 1
fi
print_info "=== 批量启动应用 ==="
print_info "准备启动 $total_count 个JAR包..."
echo ""
for jar_file in "${jar_files[@]}"; do
print_info "正在启动: $jar_file"
# 检查JAR文件是否存在
if [ ! -f "$jar_file" ]; then
print_error " JAR文件不存在: $jar_file"
continue
fi
setup_jar_variables "$jar_file"
# 检查是否已经在运行
if is_running "$jar_file"; then
print_warning " $jar_file 已经在运行"
((success_count++))
continue
fi
# 启动应用
echo " JAR包: $jar_file"
echo " JVM参数: $JAVA_OPTS"
echo " 日志文件: $LOG_FILE"
nohup java $JAVA_OPTS -jar "$jar_file" > "$LOG_FILE" 2>&1 &
sleep "$STARTUP_WAIT"
if is_running "$jar_file"; then
local pid=$(get_app_pid "$jar_file")
print_success " $jar_file 启动成功 (PID: $pid)"
((success_count++))
else
print_error " $jar_file 启动失败"
print_warning " 请检查日志文件: $LOG_FILE"
fi
echo ""
done
print_info "=== 批量启动完成 ==="
if [ $success_count -eq $total_count ]; then
print_success "成功启动: $success_count/$total_count"
else
print_warning "成功启动: $success_count/$total_count"
fi
}
# 批量停止应用
batch_stop_app() {
local jar_files=("$@")
local success_count=0
local total_count=${#jar_files[@]}
if [ $total_count -eq 0 ]; then
print_error "没有指定要停止的JAR包"
return 1
fi
print_info "=== 批量停止应用 ==="
print_info "准备停止 $total_count 个JAR包..."
echo ""
for jar_file in "${jar_files[@]}"; do
print_info "正在停止: $jar_file"
local pid=$(get_app_pid "$jar_file")
if [ -z "$pid" ]; then
print_warning " $jar_file 未运行"
((success_count++))
continue
fi
setup_jar_variables "$jar_file"
echo " 正在停止进程 (PID: $pid)..."
kill "$pid" 2>/dev/null
# 等待进程停止
if wait_for_process_stop "$pid"; then
print_success " $jar_file 停止成功"
((success_count++))
else
print_warning " 强制停止进程..."
kill -9 "$pid" 2>/dev/null
sleep "$FORCE_KILL_WAIT"
if [ -z "$(get_app_pid "$jar_file")" ]; then
print_success " $jar_file 强制停止成功"
((success_count++))
else
print_error " $jar_file 停止失败"
fi
fi
echo ""
done
print_info "=== 批量停止完成 ==="
if [ $success_count -eq $total_count ]; then
print_success "成功停止: $success_count/$total_count"
else
print_warning "成功停止: $success_count/$total_count"
fi
}
# 批量重启应用
batch_restart_app() {
local jar_files=("$@")
if [ ${#jar_files[@]} -eq 0 ]; then
print_error "没有指定要重启的JAR包"
return 1
fi
if ! check_java_environment; then
return 1
fi
print_info "=== 批量重启应用 ==="
print_info "准备重启 ${#jar_files[@]} 个JAR包..."
echo ""
# 先停止所有应用
batch_stop_app "${jar_files[@]}"
echo ""
sleep "$STARTUP_WAIT"
# 再启动所有应用
batch_start_app "${jar_files[@]}"
}
# 批量显示状态
batch_show_status() {
local jar_files=("$@")
if [ ${#jar_files[@]} -eq 0 ]; then
print_error "没有指定要查看状态的JAR包"
return 1
fi
print_info "=== 批量应用状态 ==="
echo ""
local running_count=0
local total_count=${#jar_files[@]}
for jar_file in "${jar_files[@]}"; do
echo -e "${CYAN}--- $jar_file ---${NC}"
setup_jar_variables "$jar_file"
echo "JAR包: $jar_file"
echo "JVM参数: $JAVA_OPTS"
echo "日志文件: $LOG_FILE"
local pid=$(get_app_pid "$jar_file")
if [ -n "$pid" ]; then
print_success "✓ 正在运行 (PID: $pid)"
((running_count++))
else
print_warning "✗ 未运行"
fi
echo ""
done
print_info "=== 状态统计 ==="
if [ $running_count -eq $total_count ]; then
print_success "运行中: $running_count/$total_count (全部运行)"
elif [ $running_count -eq 0 ]; then
print_warning "运行中: $running_count/$total_count (全部停止)"
else
print_warning "运行中: $running_count/$total_count (部分运行)"
fi
}
# 显示帮助信息
show_help() {
cat << 'EOF'
Java应用管理脚本
用法:
./restart-java-app.sh {start|stop|restart|status|help} [jar昵称1] [jar昵称2] ...
./restart-java-app.sh [jar昵称1] [jar昵称2] ... # 默认执行restart操作
命令说明:
start - 启动应用
stop - 停止应用
restart - 重启应用(默认操作)
status - 查看应用状态
help - 显示帮助信息
JAR包昵称说明:
• 支持模糊匹配:输入JAR包名称的一部分即可匹配
• 支持多个昵称:可以同时指定多个JAR包昵称进行批量操作
• 支持 'all' 参数:使用 'all' 可以对所有JAR包进行操作
• 自动检测:如果不指定昵称,脚本会自动检测当前目录下的JAR包
使用示例:
# 单JAR包操作
./restart-java-app.sh start # 启动(自动检测JAR包)
./restart-java-app.sh restart myapp # 重启包含"myapp"的JAR包
./restart-java-app.sh status user-service # 查看user-service状态
# 多JAR包批量操作
./restart-java-app.sh start user order # 启动user和order相关的JAR包
./restart-java-app.sh stop all # 停止所有JAR包
./restart-java-app.sh restart user order pay # 重启多个服务
# 默认重启操作(省略restart命令)
./restart-java-app.sh myapp # 重启myapp
./restart-java-app.sh user order # 重启user和order服务
配置说明:
• JAR_NAME: JAR文件名(留空自动检测)
• JAVA_OPTS: JVM参数(默认: -Xms512m -Xmx1024m)
• 日志文件: 自动生成为 {jar名称}.log
注意事项:
• 脚本需要在JAR文件所在目录运行
• 确保有足够的权限启动/停止进程
• 日志文件会在同一目录下生成
• 支持优雅停止,超时后会强制终止进程
EOF
}
# 执行命令的统一处理函数
execute_command() {
local action="$1"
shift
local jar_files=("$@")
case "$action" in
"start")
if [ ${#jar_files[@]} -gt 1 ]; then
batch_start_app "${jar_files[@]}"
else
start_app "${jar_files[0]}"
fi
;;
"stop")
if [ ${#jar_files[@]} -gt 1 ]; then
batch_stop_app "${jar_files[@]}"
else
stop_app "${jar_files[0]}"
fi
;;
"restart")
if [ ${#jar_files[@]} -gt 1 ]; then
batch_restart_app "${jar_files[@]}"
else
restart_app "${jar_files[0]}"
fi
;;
"status")
if [ ${#jar_files[@]} -gt 1 ]; then
batch_show_status "${jar_files[@]}"
else
show_status "${jar_files[0]}"
fi
;;
"help"|"-h"|"--help")
show_help
;;
*)
print_error "未知命令 '$action'"
show_help
exit 1
;;
esac
}
# ========================================
# 主程序
# ========================================
# 解析命令行参数
parse_jar_arguments "$@"
# 如果解析出了JAR包参数,执行批量操作
if [ ${#PARSED_JARS[@]} -gt 0 ]; then
# 收集所有匹配的JAR文件
all_jar_files=()
for nickname in "${PARSED_JARS[@]}"; do
matched_jars=($(find_jar_by_nickname "$nickname"))
if [ ${#matched_jars[@]} -eq 0 ]; then
print_error "未找到匹配昵称 '$nickname' 的JAR文件"
exit 1
fi
# 添加到总列表
all_jar_files+=("${matched_jars[@]}")
done
# 去重
unique_jars=($(printf '%s\n' "${all_jar_files[@]}" | sort -u))
print_info "找到 ${#unique_jars[@]} 个JAR文件:"
for jar in "${unique_jars[@]}"; do
echo " - $jar"
done
echo ""
execute_command "$PARSED_ACTION" "${unique_jars[@]}"
else
# 检查JAR文件是否存在(可能触发交互式选择)
if ! check_jar_exists; then
exit 1
fi
# 处理命令行参数
ACTION="${PARSED_ACTION:-restart}"
# 检查是否在check_jar_exists中选择了多个JAR包
if [ "$MULTI_JAR_MODE" = true ] && [ ${#SELECTED_JARS[@]} -gt 0 ]; then
print_info "检测到多JAR包模式,将对 ${#SELECTED_JARS[@]} 个JAR包执行 '$ACTION' 操作"
echo ""
execute_command "$ACTION" "${SELECTED_JARS[@]}"
else
# 单JAR包操作
execute_command "$ACTION" "$JAR_NAME"
fi
fi

Loading…
Cancel
Save