跳至主要內容

linux部署

chanchaw大约 7 分钟安装与部署

极简后台运行

nohup 表示后台运行,后面的 anhuifuyang.log 表示输出的日期写入该文件

nohup java -jar anhuifuyang.jar > anhuifuyang.log &

查看java进程

查看本地所有java进程

将下面代码保存为 java_process_check.sh ,运行后查看本地所有 java 进程

#!/bin/bash

# 检查Java进程和端口脚本
echo "正在扫描Java进程和端口信息..."
echo "=============================================="

# 检查是否以root运行,如果不是则提示
if [[ $EUID -ne 0 ]]; then
   echo "注意: 非root用户运行可能无法查看所有进程的完整信息" 
   echo "=============================================="
fi

# 查找所有Java进程
java_pids=$(ps -ef | grep java | grep -v grep | awk '{print $2}')

if [[ -z "$java_pids" ]]; then
    echo "未找到任何Java进程"
    exit 0
fi

# 计数器
count=0

# 遍历每个Java进程
for pid in $java_pids; do
    ((count++))
    echo "Java进程 #$count:"
    echo "----------------------------------------------"
    
    # 获取进程基本信息
    process_info=$(ps -p $pid -o pid,user,pcpu,pmem,cmd --no-headers 2>/dev/null)
    
    if [[ -z "$process_info" ]]; then
        echo "进程 $pid 不存在或无法访问"
        echo ""
        continue
    fi
    
    # 显示进程基本信息
    echo "PID:    $(echo $process_info | awk '{print $1}')"
    echo "用户:   $(echo $process_info | awk '{print $2}')"
    echo "CPU:    $(echo $process_info | awk '{print $3}')%"
    echo "内存:   $(echo $process_info | awk '{print $4}')%"
    
    # 获取完整的命令行
    cmd_line=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ')
    
    if [[ -n "$cmd_line" ]]; then
        # 提取jar文件路径
        jar_path=$(echo "$cmd_line" | grep -o -E '[^ ]+\.jar' | head -1)
        
        if [[ -n "$jar_path" ]]; then
            # 如果找到的是相对路径,尝试转换为绝对路径
            if [[ "$jar_path" != /* ]]; then
                # 获取进程的工作目录
                cwd=$(readlink /proc/$pid/cwd 2>/dev/null)
                if [[ -n "$cwd" ]]; then
                    absolute_jar_path="${cwd}/${jar_path}"
                    if [[ -f "$absolute_jar_path" ]]; then
                        jar_path="$absolute_jar_path"
                    fi
                fi
            fi
            echo "JAR路径: $jar_path"
        else
            echo "JAR路径: 未找到或不是jar启动"
            # 显示主类信息
            main_class=$(echo "$cmd_line" | grep -o -E '([a-zA-Z0-9_]+\.)+[a-zA-Z0-9_]+' | head -1)
            if [[ -n "$main_class" ]]; then
                echo "主类:   $main_class"
            fi
        fi
        
        # 显示完整的命令行(截断过长的行)
        echo "命令行: $(echo "$cmd_line" | cut -c1-100)$(if [ ${#cmd_line} -gt 100 ]; then echo "..."; fi)"
    else
        echo "JAR路径: 无法获取进程信息"
    fi
    
    # 获取进程使用的端口
    echo "端口:"
    ports=$(ss -tlnp 2>/dev/null | awk -v pid=$pid '$7 ~ pid {print $4}' | awk -F: '{print $NF}' | sort -nu)
    
    if [[ -n "$ports" ]]; then
        echo "$ports" | while read port; do
            echo "  - $port"
        done
    else
        # 尝试使用netstat作为备选
        ports=$(netstat -tlnp 2>/dev/null | awk -v pid=$pid '$7 ~ pid {split($4, a, ":"); print a[length(a)]}' | sort -nu)
        if [[ -n "$ports" ]]; then
            echo "$ports" | while read port; do
                echo "  - $port"
            done
        else
            echo "  - 未找到监听的TCP端口"
        fi
    fi
    
    # 获取进程启动时间
    start_time=$(ps -p $pid -o lstart --no-headers 2>/dev/null)
    if [[ -n "$start_time" ]]; then
        echo "启动时间: $start_time"
    fi
    
    echo ""
done

echo "=============================================="
echo "总共找到 $count 个Java进程"

# 显示端口汇总信息
echo ""
echo "端口汇总:"
echo "----------------------------------------------"
all_ports=$(ss -tlnp 2>/dev/null | grep java | awk '{print $4}' | awk -F: '{print $NF}' | sort -nu)

if [[ -n "$all_ports" ]]; then
    echo "$all_ports" | while read port; do
        # 获取使用该端口的进程信息
        using_pids=$(ss -tlnp 2>/dev/null | awk -v port=":$port" '$4 ~ port {print $7}' | cut -d= -f2 | cut -d, -f1 | sort -u)
        echo "端口 $port 被进程: $using_pids 使用"
    done
else
    echo "未找到Java进程监听的端口"
fi

echo "=============================================="

非systemctl

某次在 centos7 中为 FineReport 制作 systemctl 管理的守护进程时出现异常情况:使用命令启动后,查看日志,发现 FineReport 一直在自己重启,导致前端无法使用,后来换成 bash 脚本管理启停就没有问题了。下面是后来通过 AI 制作的脚本工具,修改文件开头的多个参数后可生成管理 java 进程的启停脚本

生成启停管理脚本
#!/bin/bash

# ==========================================
# 🚀 Java 启停脚本生成器
# ==========================================

# 1. 基础配置区域 (每次有新项目只需修改这里)
APP_NAME="expenseTracker"                                    # 项目名称
JAR_FULL_PATH="/projs/expenseTracker/backend/expenseTracker.jar" # JAR包绝对路径
CONFIG_FULL_PATH="/projs/expenseTracker/backend/application.yml"     # 配置文件绝对路径
OUTPUT_BIN_DIR="/projs/expenseTracker/sh"                          # 生成的脚本存放目录
LOG_DIR="/var/log"                                               # 日志存放目录
PID_DIR="/var/run"                                               # PID存放目录

# 2. JVM 参数配置,修改项目占用内存大小
JAVA_OPTS="-Xms512m -Xmx512m -Dspring.config.location=${CONFIG_FULL_PATH}"

# ==========================================
# 👇 以下为生成逻辑,通常不需要修改
# ==========================================

# 自动解析路径
JAR_FILE_NAME=$(basename "$JAR_FULL_PATH")
JAR_FILE_PATH=$(dirname "$JAR_FULL_PATH")

# 确保输出目录存在
mkdir -p "$OUTPUT_BIN_DIR"

echo "开始为项目 [$APP_NAME] 生成管理脚本..."

# ------------------------------------------
# 生成 start.sh
# ------------------------------------------
START_SCRIPT_PATH="$OUTPUT_BIN_DIR/start-${APP_NAME}.sh"
cat << EOF > "$START_SCRIPT_PATH"
#!/bin/bash

# 启动脚本配置 (自动生成)
APP_NAME="${APP_NAME}"
JAR_FILE_NAME="${JAR_FILE_NAME}"
JAR_FILE_PATH="${JAR_FILE_PATH}"
JAR_PATH="${JAR_FULL_PATH}"
CONFIG_FILE="${CONFIG_FULL_PATH}"
LOG_FILE="${LOG_DIR}/${APP_NAME}.log"
STARTUP_LOG="${LOG_DIR}/${APP_NAME}-startup.log"
PID_FILE="${PID_DIR}/${APP_NAME}.pid"
JAVA_OPTS="${JAVA_OPTS}"

$(cat << 'BODY'
# 检查必要文件是否存在
if [ ! -f "$JAR_PATH" ]; then
    echo "$(date): 错误: 找不到 JAR 文件: $JAR_PATH" >> "$STARTUP_LOG"
    exit 1
fi

if [ ! -f "$CONFIG_FILE" ]; then
    echo "$(date): 警告: 找不到配置文件: $CONFIG_FILE" >> "$STARTUP_LOG"
fi

# 检查 Java 是否安装
if ! command -v java &> /dev/null; then
    echo "$(date): 错误: Java 未安装" >> "$STARTUP_LOG"
    exit 1
fi

# 检查是否已在运行
if [ -f "$PID_FILE" ]; then
    OLD_PID=$(cat "$PID_FILE")
    if ps -p "$OLD_PID" > /dev/null 2>&1; then
        echo "$(date): 警告: $APP_NAME 已在运行 (PID: $OLD_PID)" >> "$STARTUP_LOG"
        exit 1
    else
        echo "$(date): 清理陈旧的 PID 文件" >> "$STARTUP_LOG"
        rm -f "$PID_FILE"
    fi
fi

# 切换到项目目录
cd "$JAR_FILE_PATH" || exit 1

# 启动应用
echo "$(date): 启动 $APP_NAME 服务..." >> "$STARTUP_LOG"
nohup java $JAVA_OPTS -jar "$JAR_FILE_NAME" >> "$LOG_FILE" 2>&1 &

# 记录进程 ID
PID=$!
echo $PID > "$PID_FILE"
echo "$(date): $APP_NAME 服务启动完成,PID: $PID" >> "$STARTUP_LOG"

# 验证进程是否在运行
sleep 3
if ps -p "$PID" > /dev/null 2>&1; then
    echo "$(date): 验证: 进程 $PID 正在运行" >> "$STARTUP_LOG"
else
    echo "$(date): 错误: 进程 $PID 启动后立即退出" >> "$STARTUP_LOG"
    rm -f "$PID_FILE"
    exit 1
fi
BODY
)
EOF
echo "✅ 生成成功: $START_SCRIPT_PATH"

# ------------------------------------------
# 生成 stop.sh
# ------------------------------------------
STOP_SCRIPT_PATH="$OUTPUT_BIN_DIR/stop-${APP_NAME}.sh"
cat << EOF > "$STOP_SCRIPT_PATH"
#!/bin/bash

# 关闭脚本配置 (自动生成)
APP_NAME="${APP_NAME}"
PID_FILE="${PID_DIR}/${APP_NAME}.pid"
LOG_FILE="${LOG_DIR}/${APP_NAME}.log"
SHUTDOWN_LOG="${LOG_DIR}/${APP_NAME}-shutdown.log"

$(cat << 'BODY'
# 记录关闭开始时间
echo "$(date): 开始停止 $APP_NAME 服务..." >> "$SHUTDOWN_LOG"

# 方法1: 通过 PID 文件停止进程
if [ -f "$PID_FILE" ]; then
    PID=$(cat "$PID_FILE")
    if ps -p "$PID" > /dev/null 2>&1; then
        echo "$(date): 通过 PID 文件找到进程 $PID,正在停止..." >> "$SHUTDOWN_LOG"
        kill "$PID"
        
        # 等待进程结束,最多30秒
        TIMEOUT=30
        while [ $TIMEOUT -gt 0 ] && ps -p "$PID" > /dev/null 2>&1; do
            sleep 1
            TIMEOUT=$((TIMEOUT-1))
        done
        
        # 如果进程还在,强制杀死
        if ps -p "$PID" > /dev/null 2>&1; then
            echo "$(date): 正常停止失败,强制杀死进程 $PID..." >> "$SHUTDOWN_LOG"
            kill -9 "$PID"
            sleep 2
        fi
        
        # 确认进程已停止
        if ps -p "$PID" > /dev/null 2>&1; then
            echo "$(date): 错误: 无法停止进程 $PID" >> "$SHUTDOWN_LOG"
            exit 1
        else
            echo "$(date): 成功停止进程 $PID" >> "$SHUTDOWN_LOG"
            rm -f "$PID_FILE"
        fi
    else
        echo "$(date): 警告: PID 文件存在但进程 $PID 未运行" >> "$SHUTDOWN_LOG"
        rm -f "$PID_FILE"
    fi
else
    echo "$(date): 警告: 未找到 PID 文件 $PID_FILE" >> "$SHUTDOWN_LOG"
fi
BODY
)
EOF
echo "✅ 生成成功: $STOP_SCRIPT_PATH"

# ------------------------------------------
# 生成 service.sh
# ------------------------------------------
SERVICE_SCRIPT_PATH="$OUTPUT_BIN_DIR/${APP_NAME}-service.sh"
cat << EOF > "$SERVICE_SCRIPT_PATH"
#!/bin/bash

# 服务管理脚本 (自动生成)
SCRIPT_DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
START_SCRIPT="${START_SCRIPT_PATH}"
STOP_SCRIPT="${STOP_SCRIPT_PATH}"
PID_FILE="${PID_DIR}/${APP_NAME}.pid"
APP_NAME="${APP_NAME}"
JAR_FILE_NAME="${JAR_FILE_NAME}"

$(cat << 'BODY'
case "$1" in
    start)
        echo "启动 $APP_NAME 服务..."
        "$START_SCRIPT"
        ;;
    stop)
        echo "停止 $APP_NAME 服务..."
        "$STOP_SCRIPT"
        ;;
    restart)
        echo "重启 $APP_NAME 服务..."
        "$STOP_SCRIPT"
        sleep 3
        "$START_SCRIPT"
        ;;
    status)
        if [ -f "$PID_FILE" ]; then
            PID=$(cat "$PID_FILE")
            if ps -p "$PID" > /dev/null 2>&1; then
                echo "$APP_NAME 正在运行 (PID: $PID)"
                exit 0
            else
                echo "$APP_NAME PID 文件存在但进程未运行"
                exit 1
            fi
        else
            # 检查是否有通过其他方式启动的进程
            PROCESSES=$(ps aux | grep "$JAR_FILE_NAME" | grep -v grep)
            if [ -n "$PROCESSES" ]; then
                echo "$APP_NAME 在运行但没有 PID 文件:"
                echo "$PROCESSES"
                exit 0
            else
                echo "$APP_NAME 未运行"
                exit 3
            fi
        fi
        ;;
    *)
        echo "用法: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac
BODY
)
EOF
echo "✅ 生成成功: $SERVICE_SCRIPT_PATH"

# ------------------------------------------
# 赋予可执行权限
# ------------------------------------------
chmod +x "$START_SCRIPT_PATH" "$STOP_SCRIPT_PATH" "$SERVICE_SCRIPT_PATH"
echo "🎉 全部脚本生成完毕并已赋予执行权限!"
echo "👉 您可以使用以下命令管理服务:"
echo "   $SERVICE_SCRIPT_PATH start|stop|restart|status"

制作守护进程

创建服务文件

在路径 /usr/lib/systemd/system 下创建名称为 huachongjin.service 的文件,内容如下,注意要配置真实的 java 路径以及项目路径

[Unit]
Description=imooc datav screen # 服务描述,自定义
After=syslog.target
[Service]
Type=forking # 表示后台执行
# -DSERVICE_LOG_FOLDER 表示日志文件的存放路径
# --spring.config.location 表示使用的配置文件所在的路径
# 推荐使用外部配置文件的方式,万一端口号冲突方便修改
ExecStart=/usr/local/java/bin/java -DSERVICE_LOG_FOLDER=/projs/imooc-datav/logs -jar /projs/imooc-datav/imooc-datav.jar --spring.config.location=/projs/imooc-datav/application-mult.yml
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target

指定 jvm 参数

守护进程服务文件中运行项目时也可指定 jvm 参数

ExecStart=/usr/bin/java -Xms1024m -Xmx1024m -DSERVICE_LOG_FOLDER=/data/logs -jar /usr/local/software/myapp/myapp.jar --spring.config.location=/usr/local/software/myapp/bootstrap.yml

赋予文件权限

chmod +x imooc-datav.service

# 然后使用下面代码刷新后台服务
systemctl daemon-reload

启停命令

启动sshd服务:systemctl start ssh.service
停止sshd服务:systemctl stop ssh.service
查看sshd服务状态:systemctl status ssh.service
重启sshd服务:systemctl restart ssh.service
设置开机自启动:systemctl enable ssh.service
禁止开机自启动:systemctl disable ssh.service
查看所有已经启动的服务:systemctl list-units --type=service
重新加载配置文件:systemctl daemon-reload