Spring Boot 项目启动脚本实践


placeholder image
admin 发布于:2022-05-09 10:30:22
阅读:loading

1.基本描述

Spring Boot 项目默认内置了Tomcat,打包可以直接是jar和war两种形式,若是打包jar的形式可以直接使用Java命令直接启动,但是受参数的多样和复杂等特性制约,命令在使用的过程中记忆成本较大,更多的时候是将日常应用的脚本整理待具体使用时复制粘贴,所以推荐的实现方法是以脚本文件的形式来对部署程序的启动和停止实现高度的定制化,如同Tomcat的启动和停止脚本的使用(毫无疑问无法与Tomcat的脚本实现水平相媲美),将常见的参数以变量的形式编写,启动时一键启动或者一个命令启动即可,同时一些场景也只需要传递容易记忆的参数值。一般来说启动脚本的具体实现是需要与项目代码打包紧密结合的,不妨先了解一下前篇文章的打包实现和打包效果。

2.Windows脚本介绍

本篇文章主要分享两个脚本,分别是Windows和Linux的Java程序启动脚本,Windows脚本比较简单,使用时双击bat文件打开启动即可,在启动时执行打包后的Spring Boot Application主程序,参考脚本涉及的知识点如下:

(1)docs窗口的UTF-8编码的中文正常显示;

(2)设置docs窗口的标题,当开启的窗口较多时用于识别具体窗口对应的应用程序;

(3)兼容debug模式启动,配合IDEA或Eclipse实现远程debug;

(4)使用Java命令运行脚本,当服务器存在多个JRE环境时,若需要指定某个JRE来运行此Java命令,则需要将脚本中的java命令前增加对应JRE的目录,即:%JRE_HOME%\bin\java -xxx参数。

(5)java常见的启动命令有java -jar、java -cp、java -classpath三种本例使用-cp的形式,IDEA默认启动命令使用的是-classpath的形式,而且本例中给出的debug命令参数与IDEA的debug启动也略有不同,感兴趣的可以自行查看;

2.1 脚本参考

Title = chendd-blog-examples-80

set JAVA_DEBUG=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=60606
set JAVA_OPTS=-Xmx256m -Xms256m -Xmn256m -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCTimeStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=../runtime
set JAVA_OPTS_LOGGC=-Xloggc:../logs/gc.log
set JAVA_OPTS_TEMPDIR=-Djava.io.tmpdir=../runtime
set JAVA_OPTS_ENCODING=-Dfile.encoding=utf-8
set JAVA_OPTS_TIMEZONE=-Duser.timezone=GMT+8
set JAVA_OPTS_CONF=-cp ../conf;../lib-local/*;../lib-project/*;../lib-jar/*
set JAVA_OPTS_MAIN_CLASS=cn.chendd.Bootstrap

chcp 65001

java %JAVA_DEBUG% %JAVA_OPTS% %JAVA_OPTS_LOGGC% %JAVA_OPTS_TEMPDIR% %JAVA_OPTS_ENCODING% %JAVA_OPTS_TIMEZONE% %JAVA_OPTS_CONF% %JAVA_OPTS_MAIN_CLASS%

2.2 运行示例

image.png

(双击bat文件启动)

image.png

(增加远程调试程序)

image.png

(启动远程调试程序)

启动时只有debug模式为可启动项,启动后可正常在IDEA的项目代码中断点调试,远程调试在一些场景下比较有用,本篇文章是基于IDEA + Spring Boot的远程调试,本站另有基于Eclipse + Tomcat模式的远程调试相关的文章,可自行搜索查看。

3.Linux脚本介绍

由于Linux服务器没有图形化界面,所有的操作都是通过命令行指令交互的,所以对应的脚本则显得比较复杂,使用方式为./startup start|stop|restart|status,同样支持debug模式启动,脚本的实现原理大概是脚本在启动过程时将程序启动的进程号写入程打包的根目录定义为$APP_NAME.pid文件,将进程号写入该文件;同理检查状态命令则是判断该进程编号是否存在;停止命令则是删掉对应的进程号,可以是kill -9和-15,本篇文章给出kill -15的优雅停机指令(关闭时并非强行杀掉进程,而是等待一些资源的销毁事件触发完毕后执行结束动作,优雅的关停服务主要是考虑触发类似如@PreDestory,Filter的destory,线程池的销毁,正在触发中的定时任务等)。

3.1 脚本参考

#!/bin/sh
#!/bin/bash
#
# Copyright (c) 2022 by chendd 88911006
# 特别注意:运行该命令时的pwd应该为bin目录
# All rights reserved.

#项目jar名称
APP_NAME=chendd-admin

#项目主main函数类
APP_MAIN_CLASS="cn.chendd.Bootstrap"

#JDK指定
JAVA_HOME=/app/jdk/jdk8

# 如果不指定jdk则使用默认
if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
    javaexe="$JAVA_HOME/bin/java"
elif type -p java > /dev/null 2>&1; then
    javaexe=$(type -p java)
elif [[ -x "/usr/bin/java" ]];  then
    javaexe="/usr/bin/java"
else
    echo "Unable to find Java"
    exit 1
fi

#获取当前工作空间
SOURCE="$0"
while [ -h "$SOURCE"  ]; do
    DIR="$( cd -P "$( dirname "$SOURCE"  )" && pwd  )"
    SOURCE="$(readlink "$SOURCE")"
    [[ $SOURCE != /*  ]] && SOURCE="$DIR/$SOURCE"
done
WORKING_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"

#项目目录
APP_HOME="$(dirname "$WORKING_DIR")"

#是否启用debug模式则设置为空
#APP_DEBUGE="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=60606"
APP_DEBUGE=""

#JVM参数  -Xms程序启动时占用内存大小 -Xmx程序运行期间最大可占用的内存大小 -Xss设定每个线程的堆栈大小   -Xmn 年轻代大小
JVM_OPTS="-Xmx256m -Xms256m -Xmn256m -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCTimeStamps -Xloggc:$APP_HOME/logs/gc.log -Djava.io.tmpdir=$APP_HOME/runtime -Dfile.encoding=utf-8 -Duser.timezone=GMT+8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$APP_HOME/runtime"

#项目配置文件路径
APP_CONF="$APP_HOME/conf:$APP_HOME/lib-project/*:$APP_HOME/lib-local/*:$APP_HOME/lib-jar/*"

pid=00000
#校验进程
APP_PID=$APP_HOME/$APP_NAME.pid

start(){
 checkpid
 if [ $? -eq 0 ]; then
    echo JDK路径: $JAVA_HOME
    echo 项目目录: $APP_HOME
    echo 项目名称: $APP_NAME
    echo 配置文件: $APP_CONF
    echo JVM参数: $JVM_OPTS

    if  [ ! -n "$APP_DEBUGE" ] ;then
        echo "关闭debug模式!"
    else
        echo "开启debug模式!"
    fi
    /bin/sh -c  "$javaexe $APP_DEBUGE $JVM_OPTS -cp $APP_CONF $APP_MAIN_CLASS > /dev/null 2>&1 & echo \$!" > "$APP_PID"
 echo "启动命令:$javaexe $APP_DEBUGE $JVM_OPTS -cp $APP_CONF $APP_MAIN_CLASS"
    echo "---------------------------------"
    ROOT_LOG_FILE="$APP_HOME/logs/admin/blog.root.log"
    echo "正在启动中...即将监控 $ROOT_LOG_FILE"
    echo "---------------------------------"
    for (( i = 1; i <= 60; ++i )); do
        if [[ -f "$ROOT_LOG_FILE" ]]; then
            /bin/sh -c "tail -f $ROOT_LOG_FILE"
            break
        fi
        echo "等待 $i 秒"
        sleep 1
    done

  else
      echo -e "\033[42;5m$APP_NAME is runing PID: $pid\033[0m"
  fi

}

status(){
   checkpid
   if [ $? -eq 0 ]; then
     echo -e "\033[41;5m$APP_NAME is not runing\033[0m"
   else
     echo -e "\033[42;5m$APP_NAME is runing PID: $pid\033[0m"
   fi
}

checkpid(){
   if [[ -f "$APP_PID" ]]; then
        pid=$(cat "$APP_PID")
        checkPidNum
        if [ $? -eq 0 ];then
        return 0;
        else
            return 1;
        fi
   else
        checkPidNum
        #return 0 表示不存在,1表示存在
        if [ $? -eq 0 ];then
        #echo "${?}"
        return 0;
        else
            return 1;
        fi
   fi
}

checkPidNum(){
   
    PIDNUM=`ps -ef|grep $APP_NAME|grep $APP_MAIN_CLASS|grep -v grep|awk '{print $2}'`

    if [ -z "${PIDNUM}" ]; then
        #echo "$APP_NAME $APP_MAIN_CLASS is not running."
        return 0
    else
        #echo "$APP_NAME $APP_MAIN_CLASS is running. PID: ${pid}"
        return 1
    fi
}
stop(){
    checkpid
    if [ $? -eq 0 ]; then
      echo -e "\033[41;5m$APP_NAME is not runing\033[0m"
    else
      #kill -9 $pid
      kill -15 $pid
      echo -e "\033[42;5m$APP_NAME is stoped. PID: ${pid}\033[0m"
    fi
}
restart(){
    stop
    sleep 1s
    start
}

case $1 in
          start) start;;
          stop)  stop;;
          restart)  restart;;
          status)  status;;
              *) echo -e "\033[41;5mrequire start|stop|restart|status\033[0m";;
esac

4.其它说明

(1)启动脚本中包含了gc的log文件目录,作用是启动时输出gc信息至文件中;

(2)脚本指定了tempdir的作用是指定临时目录,默认临时目录为%TEMP%位置,如File.createTempFile创建位置的位置;同时文件上传时的默认临时目录;

(3)脚本指定了编码格式为UTF-8;指定了时区为东八区的北京时间;

(4)脚本指定了conf为资源配置文件目录和其它的jar目录,优先加载项目中jar,接着接着本地jar,最后加载开源jar,目的是当修改过开源jar中的文件或者是存在同名的类路径时优先使用项目中的jar;

(5)脚本中的HeapDumpOnOutOfMemoryError参数是阿里巴巴编码规范2022年黄山版中推荐的参数,参考原文描述为:“【推荐】给 JVM 环境参数设置-XX:+HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到 OOM 场景时输出 dump 信息。说明:OOM 的发生是有概率的,甚至相隔数月才出现一例,出错时的堆内信息对解决问题非常有帮助。”

(6)脚本中默认设置了debug启动模式,且端口限定为60606,如不需要debug模式,可将对应的参数赋值删除即可;

5.源码下载

源码工程下载可转至https://gitee.com/88911006/chendd-blog-examples项目的PackageAssembly分支;


 点赞


 发表评论

当前回复:作者

 评论列表


留言区