优雅关闭SpringBoot脚本

13 阅读2分钟
#!/bin/bash

# 定义常量
UPLOAD_JAR_NAME="maycamp-backend.jar"     # 上传的jar包名称
RUN_JAR_NAME="maycamp-backend-prod.jar"   # 运行的jar包名称(带dev/prod后缀)
BACKUP_DIR="backups"                      # 备份文件目录

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

# 获取当前时间戳
get_timestamp() {
    date +"%Y%m%d-%H%M"
}

# 创建备份目录
create_backup_dir() {
    if [ ! -d "$BACKUP_DIR" ]; then
        mkdir -p "$BACKUP_DIR"
        echo "创建备份目录: $BACKUP_DIR"
    fi
}

# 备份当前运行的jar包
backup_current_jar() {
    if [ -f "$RUN_JAR_NAME" ]; then
        local timestamp=$(get_timestamp)
        local backup_name="${UPLOAD_JAR_NAME}-${timestamp}"
        local backup_path="$BACKUP_DIR/$backup_name"
        
        cp "$RUN_JAR_NAME" "$backup_path"
        echo "已备份当前运行jar包: $backup_path"
        
        # 显示备份统计信息
        show_backup_stats
    else
        echo "当前运行jar包 $RUN_JAR_NAME 不存在,跳过备份"
    fi
}

# 显示备份统计信息
show_backup_stats() {
    if [ -d "$BACKUP_DIR" ]; then
        local backup_count=$(ls -1 "$BACKUP_DIR"/* 2>/dev/null | wc -l)
        echo "当前备份总数: $backup_count 个文件"
    fi
}

# 查找应用的进程ID
find_pid() {
    local pid=$(ps -ef | grep "$RUN_JAR_NAME" | grep -v grep | awk '{print $2}')
    if [ -z "$pid" ]; then
        echo "未找到 $RUN_JAR_NAME 的运行实例,将直接启动新版本"
        return 1
    fi
    echo $pid
    return 0
}

# 优雅地关闭应用
graceful_shutdown() {
    local pid=$1
    if [ -n "$pid" ]; then
        echo "正在停止应用进程"
        # 发送 SIGTERM 信号来优雅地关闭应用
        kill -15 $pid
        echo "已发送优雅停止信号"
        return 0
    else
        echo "没有需要停止的进程"
        return 1
    fi
}

# 等待应用完全停止
wait_for_stop() {
    local pid=$1
    local timeout=60
    local count=0
    
    if [ -z "$pid" ]; then
        echo "没有需要等待停止的进程"
        return 0
    fi
    
    while [ $count -lt $timeout ]; do
        if ! ps -p $pid > /dev/null 2>&1; then
            echo "进程 $pid 已停止"
            return 0
        fi
        count=$((count + 2))
        echo "等待进程停止... ($((count))/$timeout 秒)"
        sleep 2
    done
    
    echo -e "${RED} 等待超时,进程 $pid 仍未停止,尝试强制终止...  ${NC}"
    kill -9 $pid 2>/dev/null
    sleep 2
    if ps -p $pid > /dev/null 2>&1; then
        echo -e "${RED} 错误: 无法终止进程 $pid  ${NC}"
        return 1
    else
        echo -e "${YELLOW} 进程 $pid 已强制终止  ${NC}"
        return 0
    fi
}

# 检查上传的jar包是否存在
check_upload_jar() {
    if [ ! -f "$UPLOAD_JAR_NAME" ]; then
        echo "错误: 上传的jar包 $UPLOAD_JAR_NAME 不存在!"
        echo "请确保 $UPLOAD_JAR_NAME 文件在当前目录"
        exit 1
    fi
    echo "找到上传的jar包: $UPLOAD_JAR_NAME"
}

# 启动应用
start_app() {
    echo "正在部署新版本..."
    echo "复制 $UPLOAD_JAR_NAME -> $RUN_JAR_NAME"
    cp "$UPLOAD_JAR_NAME" "$RUN_JAR_NAME"
    
    echo "启动应用中..."
    nohup java -jar "$RUN_JAR_NAME" > console.log 2>&1 &
    local new_pid=$!
    
    # 等待应用启动
    sleep 5
    
    if ps -p $new_pid > /dev/null 2>&1; then
        echo -e "${GREEN} 应用启动成功! PID: $new_pid ${NC}"
        return 0
    else
        echo -e "${RED} 错误: 应用启动失败,请检查 console.log 日志 ${NC}"
        return 1
    fi
}

# 显示部署信息
show_deployment_info() {
    echo "=========================================="
    echo "部署完成时间: $(date)"
    echo "上传的jar包: $UPLOAD_JAR_NAME"
    echo "运行的jar包: $RUN_JAR_NAME"
    echo "备份目录: $BACKUP_DIR"
    echo "=========================================="
}

# 显示当前备份列表
show_backup_list() {
    if [ -d "$BACKUP_DIR" ] && [ "$(ls -A $BACKUP_DIR 2>/dev/null)" ]; then
        echo "当前备份文件列表:"
        ls -lt "$BACKUP_DIR"/*.jar 2>/dev/null | head -10 | awk '{print "  " $6 " " $7 " " $8 " " $9}'
    else
        echo "当前没有备份文件"
    fi
}

# 主逻辑
main() {
    echo "开始部署流程..."
    echo "=========================================="
    echo "上传的jar包: $UPLOAD_JAR_NAME"
    echo "运行的jar包: $RUN_JAR_NAME"
    echo "备份目录: $BACKUP_DIR"
    echo "开始时间: $(date)"
    echo "=========================================="
    
    # 创建备份目录
    create_backup_dir
    
    # 检查上传的jar包
    check_upload_jar
    
    # 备份当前运行的jar包
    backup_current_jar
    
    # 查找并停止当前运行的应用
    local pid=$(find_pid)
    if [ -n "$pid" ] && [ "$pid" != "1" ]; then
        echo "找到运行中的实例: PID $pid"
        graceful_shutdown $pid
        wait_for_stop $pid
    else
        echo "没有运行中的实例,直接启动新版本"
    fi
    
    # 启动新版本应用
    if start_app; then
        echo -e "${GREEN} 部署成功! ${NC}"
    else
        echo -e "${RED} 部署失败! ${NC}"
        exit 1
    fi
    
    # 显示部署信息
    show_deployment_info
    show_backup_list
}

# 执行主函数
main