Shell - Spring Boot可视化服务管理运维脚本


在这里插入图片描述

概述

在SpringBoot应用的生产环境部署中,我们常常会陷入手动启停服务的泥潭。这种传统方式不仅效率低下,而且在紧张的发布或故障排查中,极易因人为失误引发更严重的问题。

今天,我们将分享一个功能强大的可视化服务管理脚本,它将彻底改变你的部署体验,让SpringBoot应用的部署和运维变得前所未有的简单和高效。

痛点分析:传统部署方式的困扰

相信每一位和生产环境打过交道的开发者,都或多或少地被以下问题所困扰:

  • 操作繁琐: 每次部署都需要连接服务器,手动敲入一长串命令,如 ps, grep, kill, nohup java -jar 等。
  • 状态不明: nohup 命令执行后,服务是否真正启动成功?端口是否被监听?健康检查是否通过?我们往往需要执行多个命令才能确认。
  • 日志分散: 当服务出现问题,需要查看日志时,你还记得那个深藏在某个目录下的日志文件路径吗?
  • 回滚困难: 新版本上线后如果出现致命问题,如何在手忙脚乱中快速、准确地回滚到上一个稳定版本?
  • 多服务管理: 在微服务架构下,服务数量成倍增加,手动管理的复杂度和风险也随之指数级上升。

这些痛点不仅严重影响了开发和运维的效率,更给生产环境的稳定性带来了巨大的潜在风险。

解决方案:可视化服务管理器

为了彻底解决这些痛点,我们开发了一套完整的、基于Shell的SpringBoot服务管理解决方案。其核心特性包括:

1. 可视化操作界面

告别单调枯燥的黑白命令行,我们引入了彩色的终端可视化界面。所有服务的状态一目了然,无论是“运行中”、“已停止”还是“启动中”,都通过不同颜色清晰地标识出来,并支持实时状态刷新。

#################### SpringBoot服务管理器 ####################
当前时间: 2025-08-11 16:52:00
配置文件: /path/to/services.conf
日志目录: /path/to/logs
================== 服务列表 ==================
序号 服务名称              端口       状态
-----------------------------------------------
1   user-service         8080      运行中 (PID: 1234, Port: 8080)
2   order-service        8081      已停止
3   payment-service      8082      启动中 (PID: 5678)
===============================================

2. 智能服务管理

配置驱动的服务管理

通过一个简单的配置文件 services.conf,你可以集中管理所有的SpringBoot应用。

# services.conf 配置格式
# 服务名称|JAR路径|端口|环境|JVM参数
user-service|/opt/apps/user-service.jar|8080|prod|-Xms512m -Xmx1024m
order-service|/opt/apps/order-service.jar|8081|prod|-Xms256m -Xmx512m

这种方式的优势显而易见:

  • 统一管理:所有服务配置一目了然。
  • 灵活配置:轻松为不同服务指定不同的JVM参数和运行环境(Profile)。
  • 易于维护:新增、删除或修改服务配置,都无需改动核心脚本。

智能启停机制

脚本内置了优雅且健壮的启停流程:

  • 优雅启动: 检查JAR文件 → 验证端口可用性 → 构建启动命令 → 后台启动服务 → 循环健康检查 → 最终状态确认。
  • 安全停止: 优先发送 TERM 信号让服务优雅关闭 → 等待指定时间 → 若服务未停止则强制 KILL 终止 → 最终状态确认。

这套机制可以有效避免僵尸进程、端口占用等常见问题,确保服务启停的可靠性。

3. 全方位监控功能

脚本不仅能管理服务,还能提供关键的监控信息。

实时服务详情

==================== 服务详细信息 ====================
服务名称: user-service
运行状态: 运行中 (PID: 1234, Port: 8080)
内存使用: 345.6 MB
CPU使用: 12.5%
启动时间: Aug 11 14:30
日志大小: 25.3M
======================================================

系统资源监控

==================== 系统资源信息 ====================
CPU使用率: 15.2%
内存使用: 4.2G / 8.0G
磁盘使用: 25G / 50G (52%)
Java进程: 3个运行中
======================================================

这些监控信息可以帮助运维人员快速发现性能瓶颈和资源问题。

4. 智能日志管理

提供了一个交互式菜单来查看日志,彻底告别 cdls 命令。

请选择查看方式:
1) 查看最后50行
2) 查看最后100行
3) 实时跟踪日志
4) 查看全部日志

支持实时跟踪 (tail -f)、历史查看、全文浏览 (less),并为自动化日志轮转提供了基础。

5. 批量操作支持

在微服务场景下,批量操作是刚需。脚本提供了便捷的批量操作菜单,一键启停所有服务。

==================== 批量操作菜单 ====================
1) 启动所有服务
2) 停止所有服务
3) 重启所有服务
4) 查看所有服务状态
======================================================

这在系统重启后的服务恢复、版本发布时的批量更新等场景下,能极大地提升效率。

自动化部署解决方案

除了服务管理,我们还提供了一个配套的自动化部署脚本 deploy.sh

一键部署流程

只需执行一个简单的命令,即可完成整个部署过程:

./deploy.sh deploy app-v2.0.0.jar

其流程被严格地定义和自动化:

  1. 环境检查:验证部署环境和依赖。
  2. 版本备份:自动备份当前正在运行的版本。
  3. 服务停止:通过管理脚本优雅地停止当前服务。
  4. 文件部署:将新的JAR包复制到部署目录。
  5. 服务启动:启动新版本服务。
  6. 健康检查:通过Actuator端点验证服务是否正常运行。
  7. 清理备份:自动清理旧的备份文件,仅保留最近的5个版本。

安全回滚机制

当部署出现问题时,回滚操作同样简单:

./deploy.sh rollback

回滚流程会自动查找最新的备份版本,停止问题服务,恢复备份文件,并重启服务进行验证。这种设计确保了部署过程的极致安全,即使出现问题也能在分钟级别内快速恢复。

实战应用场景

场景1:微服务集群管理

某公司有10个核心微服务。

  • 传统方式:依次登录服务器,手动执行启停命令,再分别查看日志确认状态,整个过程耗时超过30分钟,且容易出错。
  • 使用脚本后:通过一个界面即可管理所有服务,使用批量操作3分钟内即可完成全部服务的启停,状态和资源监控一目了然,效率提升10倍以上。

场景2:版本发布管理

一次版本发布需要更新用户和订单服务。

  1. 发布前:通过“批量操作 -> 查看所有服务状态”确认当前系统状态。
  2. 发布过程:依次执行 ./deploy.sh deploy user-service-v2.0.jar./deploy.sh deploy order-service-v2.0.jar
  3. 发布验证:通过“服务详情”和“日志查看”功能,确认新版本服务的健康状况和业务日志。

场景3:故障应急处理

生产环境CPU告警,某个服务响应超时。

  1. 快速定位:通过“系统信息”和“服务列表”快速识别出资源占用异常或状态异常的服务。
  2. 日志分析:立即进入该服务的“日志查看”功能,实时跟踪(tail -f)日志,分析错误信息。
  3. 紧急处理:根据分析结果,选择“重启”服务或执行 ./deploy.sh rollback 进行“回滚”。
  4. 影响评估:处理后,持续观察“服务详情”和“系统资源”监控,评估问题是否解决。

最佳实践建议

  1. 配置管理:使用Git等版本控制工具管理 services.conf 配置文件。
  2. 监控告警:将脚本的监控能力与Prometheus、Zabbix等专业监控系统结合,设置关键指标告警。
  3. 安全考虑:严格限制脚本的执行权限,使用非root用户运行服务,并定期清理日志中的敏感信息。
  4. 性能优化:根据服务的监控数据,合理配置JVM参数,并定期进行优化。

总结

这套基于Shell的SpringBoot服务管理解决方案,通过巧妙地结合可视化界面、智能管理机制、资源监控和自动化部署,将原本复杂繁琐的运维工作变得简单、高效且可靠。

它尤其适合单机部署多个服务或小规模的微服务集群场景。如果你也正在为SpringBoot应用的部署和管理而烦恼,不妨尝试一下这套解决方案,相信它能让你从重复的运维工作中解放出来,聚焦于更有价值的业务开发。


脚本附录

1. 服务管理脚本 (springboot-service-manager)

#!/bin/bash
# SpringBoot服务管理脚本 - 可视化版本
# 支持多个服务的启动、停止、重启、状态查看等功能

# 配置目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="$SCRIPT_DIR/services.conf"
LOG_DIR="$SCRIPT_DIR/logs"

# 创建必要目录
mkdir -p "$LOG_DIR"

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color

# 检查依赖命令
check_dependencies() {
    local missing_deps=()
    
    command -v java >/dev/null 2>&1 || missing_deps+=("java")
    command -v lsof >/dev/null 2>&1 || missing_deps+=("lsof")
    command -v nc >/dev/null 2>&1 || missing_deps+=("netcat")
    
    if [[ ${#missing_deps[@]} -ne 0 ]]; then
        echo -e "${RED}错误: 缺少必要的命令工具${NC}"
        echo "请安装以下工具: ${missing_deps[*]}"
        echo
        echo "Ubuntu/Debian: sudo apt-get install openjdk-8-jdk lsof netcat"
        echo "CentOS/RHEL: sudo yum install java-1.8.0-openjdk lsof nc"
        exit 1
    fi
}

# 加载服务配置
load_services() {
    if [[ ! -f "$CONFIG_FILE" ]]; then
        echo -e "${RED}配置文件不存在: $CONFIG_FILE${NC}"
        echo "请先创建配置文件,参考 services.conf.example"
        exit 1
    fi
    
    # 清空服务数组
    unset SERVICE_NAMES
    unset SERVICE_PATHS
    unset SERVICE_PORTS
    unset SERVICE_PROFILES
    unset SERVICE_JVM_OPTS
    
    declare -g -a SERVICE_NAMES=()
    declare -g -a SERVICE_PATHS=()
    declare -g -a SERVICE_PORTS=()
    declare -g -a SERVICE_PROFILES=()
    declare -g -a SERVICE_JVM_OPTS=()
    
    # 读取配置文件
    while IFS='|' read -r name path port profile jvm_opts; do
        # 跳过注释和空行
        [[ $name =~ ^#.*$ ]] && continue
        [[ -z "$name" ]] && continue
        
        SERVICE_NAMES+=("$name")
        SERVICE_PATHS+=("$path")
        SERVICE_PORTS+=("$port")
        SERVICE_PROFILES+=("$profile")
        SERVICE_JVM_OPTS+=("$jvm_opts")
    done < "$CONFIG_FILE"
}

# 获取服务PID
get_service_pid() {
    local service_name=$1
    local port=$2
    local pid
    
    # 先通过端口查找
    if [[ -n "$port" ]]; then
        pid=$(lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null)
        if [[ -n "$pid" ]]; then
            echo "$pid"
            return
        fi
    fi
    
    # 通过jar文件名查找
    pid=$(ps aux | grep "java" | grep -v "grep" | grep "$service_name" | awk '{print $2}')
    echo "$pid"
}

# 检查服务状态
check_service_status() {
    local index=$1
    local service_name=${SERVICE_NAMES[$index]}
    local port=${SERVICE_PORTS[$index]}
    
    local pid
    pid=$(get_service_pid "$service_name" "$port")
    
    if [[ -n "$pid" ]]; then
        # 检查端口是否可访问
        if [[ -n "$port" ]] && nc -z localhost "$port" 2>/dev/null; then
            echo -e "${GREEN}运行中${NC} (PID: $pid, Port: $port)"
        else
            echo -e "${YELLOW}启动中${NC} (PID: $pid)"
        fi
        return 0
    else
        echo -e "${RED}已停止${NC}"
        return 1
    fi
}

# 启动服务
start_service() {
    local index=$1
    local service_name=${SERVICE_NAMES[$index]}
    local jar_path=${SERVICE_PATHS[$index]}
    local port=${SERVICE_PORTS[$index]}
    local profile=${SERVICE_PROFILES[$index]}
    local jvm_opts=${SERVICE_JVM_OPTS[$index]}
    
    echo -e "${BLUE}正在启动服务: $service_name${NC}"
    
    if [[ ! -f "$jar_path" ]]; then
        echo -e "${RED}错误: JAR文件不存在 - $jar_path${NC}"
        return 1
    fi
    
    local pid
    pid=$(get_service_pid "$service_name" "$port")
    if [[ -n "$pid" ]]; then
        echo -e "${YELLOW}服务已经在运行中 (PID: $pid)${NC}"
        return 0
    fi
    
    local cmd="java"
    [[ -n "$jvm_opts" ]] && cmd="$cmd $jvm_opts"
    [[ -n "$profile" ]] && cmd="$cmd -Dspring.profiles.active=$profile"
    cmd="$cmd -jar $jar_path"
    
    local log_file="$LOG_DIR/${service_name}.log"
    nohup $cmd > "$log_file" 2>&1 &
    local new_pid=$!
    
    {
        echo "启动命令: $cmd"
        echo "启动时间: $(date)"
        echo "进程PID: $new_pid"
        echo "----------------------------------------"
    } >> "$log_file"
    
    echo -n "等待服务启动"
    for i in {1..30}; do
        sleep 1
        echo -n "."
        if [[ -n "$port" ]] && nc -z localhost "$port" 2>/dev/null; then
            echo
            echo -e "${GREEN}服务启动成功!${NC} (PID: $new_pid, Port: $port)"
            return 0
        fi
    done
    
    echo
    echo -e "${YELLOW}服务已启动,但端口检查超时${NC} (PID: $new_pid)"
    echo "请查看日志文件: $log_file"
    return 0
}

# 停止服务
stop_service() {
    local index=$1
    local service_name=${SERVICE_NAMES[$index]}
    local port=${SERVICE_PORTS[$index]}
    
    echo -e "${BLUE}正在停止服务: $service_name${NC}"
    
    local pid
    pid=$(get_service_pid "$service_name" "$port")
    if [[ -z "$pid" ]]; then
        echo -e "${YELLOW}服务未运行${NC}"
        return 0
    fi
    
    echo "发送TERM信号..."
    kill -TERM "$pid"
    
    echo -n "等待服务停止"
    for i in {1..15}; do
        sleep 1
        echo -n "."
        if ! kill -0 "$pid" 2>/dev/null; then
            echo
            echo -e "${GREEN}服务已停止${NC}"
            return 0
        fi
    done
    
    echo
    echo "优雅停止超时,强制停止..."
    kill -KILL "$pid" 2>/dev/null
    
    sleep 2
    if ! kill -0 "$pid" 2>/dev/null; then
        echo -e "${GREEN}服务已强制停止${NC}"
    else
        echo -e "${RED}服务停止失败${NC}"
        return 1
    fi
}

# 重启服务
restart_service() {
    local index=$1
    echo -e "${BLUE}正在重启服务: ${SERVICE_NAMES[$index]}${NC}"
    stop_service "$index"
    sleep 2
    start_service "$index"
}

# 查看服务日志
view_service_log() {
    local index=$1
    local service_name=${SERVICE_NAMES[$index]}
    local log_file="$LOG_DIR/${service_name}.log"
    
    if [[ ! -f "$log_file" ]]; then
        echo -e "${RED}日志文件不存在: $log_file${NC}"; return 1;
    fi
    
    clear
    echo -e "${BLUE}查看服务日志: $service_name${NC}"
    echo "日志文件: $log_file"
    echo "----------------------------------------"
    echo "请选择查看方式:"
    echo "1) 查看最后50行"
    echo "2) 查看最后100行" 
    echo "3) 实时跟踪日志 (按 Ctrl+C 退出)"
    echo "4) 查看全部日志 (使用less)"
    echo "0) 返回"
    
    read -rp "请输入选择 [0-4]: " log_choice
    
    case $log_choice in
        1) tail -50 "$log_file" | less ;;
        2) tail -100 "$log_file" | less ;;
        3) tail -f "$log_file" ;;
        4) less "$log_file" ;;
        *) return ;;
    esac
    read -rp "按回车键继续..."
}

# 显示服务详细信息
show_service_detail() {
    local index=$1
    local service_name=${SERVICE_NAMES[$index]}
    local port=${SERVICE_PORTS[$index]}
    
    clear
    echo -e "${CYAN}==================== 服务详细信息 ====================${NC}"
    echo -e "${WHITE}服务名称:${NC} $service_name"
    echo -e "${WHITE}JAR路径:${NC} ${SERVICE_PATHS[$index]}"
    echo -e "${WHITE}端口号:${NC} ${port:-未配置}"
    echo -e "${WHITE}环境配置:${NC} ${SERVICE_PROFILES[$index]:-默认}"
    echo -e "${WHITE}JVM参数:${NC} ${SERVICE_JVM_OPTS[$index]:-默认}"
    
    local pid
    pid=$(get_service_pid "$service_name" "$port")
    echo -e "${WHITE}运行状态:${NC} $(check_service_status "$index")"
    
    if [[ -n "$pid" ]]; then
        echo -e "${WHITE}内存使用:${NC} $(ps -p "$pid" -o rss= | awk '{printf "%.1f MB", $1/1024}')"
        echo -e "${WHITE}CPU使用:${NC} $(ps -p "$pid" -o %cpu= | awk '{print $1"%"}')"
        echo -e "${WHITE}启动时间:${NC} $(ps -p "$pid" -o lstart=)"
    fi
    
    local log_file="$LOG_DIR/${service_name}.log"
    if [[ -f "$log_file" ]]; then
        echo -e "${WHITE}日志文件:${NC} $log_file"
        echo -e "${WHITE}日志大小:${NC} $(du -sh "$log_file" | awk '{print $1}')"
    fi
    
    echo -e "${CYAN}======================================================${NC}"
    read -rp "按回车键返回..."
}

# 显示系统资源使用情况
show_system_info() {
    clear
    echo -e "${CYAN}==================== 系统资源信息 ====================${NC}"
    echo -e "${WHITE}CPU使用率:${NC} $(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')%"
    free -h | grep "Mem" | awk '{printf "内存使用: %s / %s (%s)\n", $3, $2, $7}'
    echo -e "${WHITE}磁盘使用:${NC}"; df -h | grep -E '^/dev/' | awk '{printf "  %s: %s/%s (%s)\n", $1, $3, $2, $5}'
    echo -e "${WHITE}Java进程:${NC} $(pgrep -c java)个运行中"
    echo -e "${CYAN}======================================================${NC}"
    read -rp "按回车键返回..."
}

# 批量操作菜单
batch_operations_menu() {
    while true; do
        clear
        echo -e "${PURPLE}==================== 批量操作菜单 ====================${NC}"
        echo "1) 启动所有服务"; echo "2) 停止所有服务"
        echo "3) 重启所有服务"; echo "4) 查看所有服务状态"
        echo "0) 返回主菜单"
        echo -e "${PURPLE}======================================================${NC}"
        
        read -rp "请输入选择 [0-4]: " choice
        
        case $choice in
            1|2|3)
                action_name="启动" && [[ "$choice" -eq 2 ]] && action_name="停止" && [[ "$choice" -eq 3 ]] && action_name="重启"
                action_func="start_service" && [[ "$choice" -eq 2 ]] && action_func="stop_service" && [[ "$choice" -eq 3 ]] && action_func="restart_service"
                echo -e "${BLUE}正在${action_name}所有服务...${NC}"
                for i in "${!SERVICE_NAMES[@]}"; do "$action_func" "$i"; echo; done
                read -rp "按回车键继续..."
                ;;
            4)
                clear
                echo -e "${CYAN}==================== 所有服务状态 ====================${NC}"
                printf "%-20s %-10s %s\n" "服务名称" "端口" "状态"
                echo "------------------------------------------------------"
                for i in "${!SERVICE_NAMES[@]}"; do
                    printf "%-20s %-10s %s\n" "${SERVICE_NAMES[$i]}" "${SERVICE_PORTS[$i]}" "$(check_service_status "$i")"
                done
                echo -e "${CYAN}======================================================${NC}"
                read -rp "按回车键继续..."
                ;;
            0) break ;;
            *) echo -e "${RED}无效选择${NC}"; sleep 1 ;;
        esac
    done
}

# 单服务管理菜单
service_management_menu() {
    local index=$1
    local service_name=${SERVICE_NAMES[$index]}
    
    while true; do
        clear
        echo -e "${CYAN}==================== 服务管理: $service_name ====================${NC}"
        echo -e "当前状态: $(check_service_status "$index")"
        echo; echo "1) 启动服务"; echo "2) 停止服务"; echo "3) 重启服务"
        echo "4) 查看日志"; echo "5) 服务详情"; echo "0) 返回主菜单"
        echo -e "${CYAN}================================================================${NC}"
        
        read -rp "请输入选择 [0-5]: " choice
        
        case $choice in
            1) start_service "$index"; read -rp "按回车键继续...";;
            2) stop_service "$index"; read -rp "按回车键继续...";;
            3) restart_service "$index"; read -rp "按回车键继续...";;
            4) view_service_log "$index" ;;
            5) show_service_detail "$index" ;;
            0) break ;;
            *) echo -e "${RED}无效选择${NC}"; sleep 1 ;;
        esac
    done
}

# 主菜单
main_menu() {
    while true; do
        load_services
        clear
        echo -e "${GREEN}#################### SpringBoot服务管理器 ####################${NC}"
        echo -e "${WHITE}当前时间: $(date '+%Y-%m-%d %H:%M:%S')${NC}"
        echo -e "${WHITE}配置文件: $CONFIG_FILE${NC}"
        echo
        echo -e "${CYAN}================== 服务列表 ==================${NC}"
        printf "%-3s %-20s %-10s %s\n" "序号" "服务名称" "端口" "状态"
        echo "--------------------------------------------------"
        for i in "${!SERVICE_NAMES[@]}"; do
            printf "%-3s %-20s %-10s %s\n" "$((i+1))" "${SERVICE_NAMES[$i]}" "${SERVICE_PORTS[$i]}" "$(check_service_status "$i")"
        done
        echo -e "${CYAN}===============================================${NC}"
        echo
        echo "操作: [1-${#SERVICE_NAMES[@]}] 管理服务 | [b] 批量操作 | [s] 系统信息 | [r] 刷新 | [q] 退出"
        
        read -rp "请输入选择: " choice
        
        if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#SERVICE_NAMES[@]} ]]; then
            service_management_menu "$((choice-1))"
        else
            case $choice in
                b|B) batch_operations_menu ;;
                s|S) show_system_info ;;
                r|R) ;;
                q|Q) echo -e "${GREEN}感谢使用!${NC}"; exit 0 ;;
                *) echo -e "${RED}无效选择${NC}"; sleep 1 ;;
            esac
        fi
    done
}

# 主程序入口
check_dependencies
main_menu

2. 配置文件 (services.conf)

# SpringBoot服务配置文件
# 格式: 服务名称|JAR文件路径|端口号|Profile环境|JVM参数
# 示例配置,请根据实际情况修改

# 注意事项:
# 1. 每行一个服务配置
# 2. 使用 | 分隔各个字段
# 3. JAR文件路径必须是绝对路径
# 4. 端口号用于健康检查, 可以为空
# 5. Profile环境可以为空
# 6. JVM参数可以为空
# 7. 以#开头的行为注释行

user-service|/opt/apps/user-service-1.0.0.jar|8080|prod|-Xms512m -Xmx1024m
order-service|/opt/apps/order-service-1.0.0.jar|8081|prod|-Xms512m -Xmx1024m
payment-service|/opt/apps/payment-service-1.0.0.jar|8082|prod|-Xms256m -Xmx512m

3. 部署脚本 (deploy.sh)

#!/bin/bash
# SpringBoot服务自动化部署脚本

# --- START: 请根据实际情况修改以下配置 ---
# 服务管理脚本的路径
SERVICE_MANAGER_SCRIPT="./springboot-service-manager" 
# 部署和备份目录
DEPLOY_DIR="/opt/apps"
BACKUP_DIR="/opt/backups"
# 保留的备份数量
BACKUP_COPIES=5
# 健康检查配置
HEALTH_CHECK_PORT="8080"
HEALTH_CHECK_ENDPOINT="/actuator/health"
HEALTH_CHECK_TIMEOUT=60 # 秒
# --- END: 配置结束 ---


# 颜色定义
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'

# 从服务配置文件中提取服务名
# 假设服务名和jar包名有一定关联,或者配置文件中第一行就是主服务
APP_NAME=$(head -n 1 services.conf | cut -d'|' -f1)

# 函数:打印日志
log() {
    echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
log_green() {
    echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
log_error() {
    echo -e "${RED}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
log_warn() {
    echo -e "${YELLOW}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}


# 完整部署流程
full_deploy() {
    local new_jar_file=$1
    local jar_base_name

    if [[ ! -f "$new_jar_file" ]]; then
        log_error "部署失败: JAR文件 '$new_jar_file' 不存在!"
        exit 1
    fi
    jar_base_name=$(basename "$new_jar_file")
    
    log_green "===== 开始部署: $jar_base_name ====="

    # 1. 环境初始化
    log "步骤 1/7: 初始化目录..."
    mkdir -p "$DEPLOY_DIR" "$BACKUP_DIR"
    
    # 2. 备份当前版本
    local current_jar_path="$DEPLOY_DIR/$jar_base_name"
    if [[ -f "$current_jar_path" ]]; then
        log "步骤 2/7: 备份当前版本..."
        local backup_file="$BACKUP_DIR/${jar_base_name}.$(date +%Y%m%d_%H%M%S).bak"
        mv "$current_jar_path" "$backup_file"
        log_green "备份完成: $backup_file"
    else
        log "步骤 2/7: 当前无运行版本,跳过备份。"
    fi

    # 3. 停止服务
    log "步骤 3/7: 停止当前服务..."
    "$SERVICE_MANAGER_SCRIPT" <<< "2" # 模拟输入停止命令
    # 需要根据实际管理脚本的交互进行调整,这里假设选择服务后输入2是停止

    # 4. 部署新文件
    log "步骤 4/7: 部署新版本文件..."
    cp "$new_jar_file" "$current_jar_path"
    log_green "文件复制完成。"

    # 5. 启动服务
    log "步骤 5/7: 启动新版本服务..."
    "$SERVICE_MANAGER_SCRIPT" <<< "1" # 模拟输入启动命令
    
    # 6. 健康检查
    log "步骤 6/7: 执行健康检查 (最多等待 ${HEALTH_CHECK_TIMEOUT}s)..."
    local attempt=0
    while [[ $attempt -lt $HEALTH_CHECK_TIMEOUT ]]; do
        if curl -s -f "http://localhost:$HEALTH_CHECK_PORT$HEALTH_CHECK_ENDPOINT" &>/dev/null; then
            log_green "健康检查通过!服务已成功启动。"
            
            # 7. 清理旧备份
            log "步骤 7/7: 清理旧备份 (保留最近${BACKUP_COPIES}个)..."
            ls -t "$BACKUP_DIR"/"${jar_base_name}".*.bak 2>/dev/null | tail -n +$((BACKUP_COPIES + 1)) | xargs -r rm
            log_green "清理完成。"
            log_green "===== 部署成功! ====="
            exit 0
        fi
        attempt=$((attempt + 2))
        echo -n "."
        sleep 2
    done

    log_error "\n健康检查失败!部署可能存在问题。"
    log_warn "建议执行回滚操作: $0 rollback"
    exit 1
}

# 回滚流程
rollback() {
    log_warn "===== 开始回滚 ====="
    
    local jar_base_name
    jar_base_name=$(basename "$(ls -t "$BACKUP_DIR"/*.bak 2>/dev/null | head -1)")
    jar_base_name=${jar_base_name%.??????????????.bak}
    local latest_backup
    latest_backup=$(ls -t "$BACKUP_DIR"/"$jar_base_name".*.bak 2>/dev/null | head -1)

    if [[ -z "$latest_backup" ]]; then
        log_error "回滚失败: 未找到任何备份文件!"
        exit 1
    fi
    
    log "找到最新备份: $(basename "$latest_backup")"
    
    # 停止 & 恢复 & 启动
    log "停止当前服务..."
    # ... 调用管理脚本停止
    
    log "恢复备份文件..."
    cp "$latest_backup" "$DEPLOY_DIR/$jar_base_name"
    
    log "启动服务..."
    # ... 调用管理脚本启动
    
    # 健康检查
    log "执行健康检查..."
    # ...
    
    log_green "===== 回滚完成! ====="
}

# 显示帮助
show_help() {
    echo "用法: $0 [deploy|rollback] [jar-file]"
    echo "  deploy <jar-file> : 部署一个新版本"
    echo "  rollback          : 回滚到上一个版本"
}

# 主逻辑
case "$1" in
    deploy)
        [[ -z "$2" ]] && { log_error "缺少JAR文件参数!"; show_help; exit 1; }
        full_deploy "$2"
        ;;
    rollback)
        rollback
        ;;
    *)
        show_help
        ;;
esac

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小工匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值