1. 背景
由于实际项目需要,需要在 Linux 下发部署 Java Web 应用。因为此前都是通过 Weblogic来部署的,乍换一种方式,有点不使用。
主要遇到问题有三个:
- 启动过程有点繁琐,不够简单易用,对维护人员有一定要求,去理解每个参数的设置
- 而且启动参数,每次重启都要输入一堆参数,不够便捷
- 再者,重启过程需要找到进程 然后 kill 掉,再重启,耗时多
2. 解决思路
通过网上材料,无非通过 K8S 去管理部署应用 以及 原生 Java 方式管理,但是我们的 K8S 环境还没提供,只能暂时采用 Java 原生。
Java 原生,参数太多,那考虑将这些参数封装成为一个 shell 脚本,以后服务的启动、停、重启或者查看状态,都是通过 一个 shell 脚本来完成。
Shell 脚本功能就是提供应用的 启动、停、重启或者查看状态。那就是写四个方法,分别让用户选择。
3. 实现举措
四个核心方法,完成应用的 启动、停、重启或者查看状态。
3.1. 核心方法
function start()
{
}
function stop()
{
}
function restart()
{
}
function status()
{
}
3.2. 启动
启动过程中指定 JVM
参数,这里提供参考 JVM_OPTS="-Dname=$SpringBoot -Duser.timezone=Asia/Shanghai -Xms1024M -Xmx1024M -XX:PermSize=256M -XX:MaxPermSize=768M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -Xloggc:$GC_LOG_PATH -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
设置 虚拟机内容、和 内容异常过程中Dump操作、以及设置 GC日志路径。
3.3. 停止
应用停止过程中需要判断应用当前的状态,通过
ps -ef |grep $SpringBoot |grep 'java -jar'|grep -v grep|awk '{print $2}'
检查出来应用的 PID 。通过
ps -ef |grep $SpringBoot |grep 'java -jar'|grep -v grep|awk '{print $2}' | xargs kill
来停止应用
3.4. 重启
结合 启动和停止,查询应用状态,服务存在的话,则 kill 应用,然后再启动应用。
3.4.1. 查看状态
应用停止过程中需要判断应用当前的状态,通过 ps -ef |grep $SpringBoot |grep 'java -jar'|grep -v grep|awk '{print $2}'
检查出来应用的 PID 。
3.5. 其他
3.5.1. 日志路径
默认在当前应用的目录下构建 logs
日志文件夹,并按照应用名称,分目录存储。
如应用 A
,则日志文件在 logs/A/
下
3.5.2. 格式化日志
利用 function log_*
方法,定义日志的级别。
LOG_LEVEL=1
function log_debug(){
content="[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 1 ] && echo -e "\033[32m" ${content} "\033[0m"
}
function log_info(){
content="[INFO] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 2 ] && echo -e "\033[32m" ${content} "\033[0m"
}
function log_warn(){
content="[WARN] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 3 ] && echo -e "\033[33m" ${content} "\033[0m"
}
function log_err(){
content="[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 4 ] && echo -e "\033[31m" ${content} "\033[0m"
}
function log_always(){
content="[ALWAYS] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 5 ] && echo -e "\033[32m" ${content} "\033[0m"
}
4. 完整文件
#!/bin/bash
# 日志级别 debug-1, info-2, warn-3, error-4, always-5
LOG_LEVEL=1
# 调试日志
function log_debug(){
content="[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 1 ] && echo -e "\033[32m" ${content} "\033[0m"
}
# 信息日志
function log_info(){
content="[INFO] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 2 ] && echo -e "\033[32m" ${content} "\033[0m"
}
# 警告日志
function log_warn(){
content="[WARN] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 3 ] && echo -e "\033[33m" ${content} "\033[0m"
}
# 错误日志
function log_err(){
content="[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 4 ] && echo -e "\033[31m" ${content} "\033[0m"
}
# 一直都会打印的日志
function log_always(){
content="[ALWAYS] $(date '+%Y-%m-%d %H:%M:%S') $@"
[ $LOG_LEVEL -le 5 ] && echo -e "\033[32m" ${content} "\033[0m"
}
SpringBoot=$1
if [ "$SpringBoot" = "" ];
then
log_err "Please enter the Jar application name"
lot=$(find ./ -maxdepth 1 -type f -and -name "*.jar")
# lot_pat=${lot#*/}
log_err "The Optional Jar applications are as follows: $lot"
exit 1
fi
ADATE=$(date +%Y%m%d%H%M%S)
# 启动参数
START_OPTS=$3
# JVM参数
APP_HOME=$(pwd)
dirname $0|grep "^/" >/dev/null
# 获取当前执行路径
if [ $? -eq 0 ];then
APP_HOME=$(DIR_NAME $0)
else
dirname $0|grep "^\." >/dev/null
retval=$?
if [ $retval -eq 0 ];then
APP_HOME=$(dirname $0|sed "s#^.#$APP_HOME#")
else
APP_HOME=$(dirname $0|sed "s#^#$APP_HOME/#")
fi
fi
log_info "Current directory is $APP_HOME"
ENV_PORT=${START_OPTS#*=}
ENV_DIR="$APP_HOME/logs"
if [ "$ENV_PORT" = "" ]; then
log_info "Application Port is Null"
log_info "$ENV_DIR"
if [ ! -d "$ENV_DIR" ];then
mkdir -p $ENV_DIR
fi
else
log_info "Application Port is $ENV_PORT"
ENV_DIR="$ENV_DIR/$ENV_PORT"
log_info "$ENV_DIR"
if [ ! -d "$ENV_DIR" ];then
mkdir -p $ENV_DIR
fi
fi
log_warn " Construct log folder $ENV_DIR"
pid=0
Purpose=$2
# 当没有输入具体,操作,默认为 START ,此时需要用户二次确认,输入 Y|y|YES|Yes 同意操作
# 或者 n|N|NO|no ,不同意重启,直接退出
if [ "$Purpose" = "" ];
then
log_err "Operation Name Not Entered : The Default Action Is START !"
read -p "Are You Sure?[y/n]:" sure
case $sure in
y|Y|Yes|YES)
log_warn "You Enter $a"
log_warn "Prepare to restart the app: $SpringBoot"
Purpose="start"
;;
n|N|NO|no)
echo "you enter $a"
log_warn "Ready To Exit Startup: $SpringBoot"
exit 1
;;
*)
echo "error";;
esac
fi
log_debug "##############################"
log_debug "Java environment variable information"
LOG_PATH=$ENV_DIR/$SpringBoot-$ADATE.log
GC_LOG_PATH=$ENV_DIR/gc-$SpringBoot-$ADATE.log
VSpringBoot=${SpringBoot%%.*}
LOG_DEBUG_PATH=$ENV_DIR/$VSpringBoot/debug.log
log_debug "LOG_DEBUG_PATH is $LOG_DEBUG_PATH"
log_debug "$(java -version)"
log_debug "Startup log $LOG_PATH"
log_debug "Startup gc log $GC_LOG_PATH"
JVM_OPTS="-Dname=$SpringBoot -Duser.timezone=Asia/Shanghai -Xms1024M -Xmx1024M -XX:PermSize=256M -XX:MaxPermSize=768M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -Xloggc:$GC_LOG_PATH -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
log_debug "$JVM_OPTS $GC_LOG_PATH"
# 启动项目,如果项目已经启动过,则先 kill 掉原先项目,再启动
function start()
{
# 检查项目的进程是否存在
checkPid
#
if [ $pid -ne "0" ]; then
log_info "Application $SpringBoot Running... PID:$pid ,Please Stop It"
# echo -e "\033[31m Application $SpringBoot Running... PID:$pid ,Please Stop It \033[0m"
# Kill the current Process
killPid
log_info ".............."
startFun
else
log_info ".............."
startFun
log_info ".............."
fi
}
checkPid()
{
pid=$(ps -ef |grep $SpringBoot |grep 'java -jar'|grep -v grep|awk '{print $2}')
# `ps -aux | grep $SpringBoot | grep 'java -jar'|grep -v grep | awk '{print $2}' | xargs kill`
}
killPid()
{
log_err "Application PID:$pid is being stopped, Please wait for a while, or fish"
# echo -e "\033[31m Application PID:$pid is being stopped, Please wait for a while, or fish \033[0m"
$(ps -ef |grep $SpringBoot |grep 'java -jar'|grep -v grep|awk '{print $2}' | xargs kill)
tail -n 10 $LOG_DEBUG_PATH
sleep 10s
}
startFun()
{
log_debug "Begin Start $SpringBoot ..."
# echo -e "\033[32m Begin Start $SpringBoot ... \033[0m"
$(java -jar $JVM_OPTS $SpringBoot $START_OPTS > $LOG_PATH 2>&1 &)
# nohup java -jar $JVM_OPTS $SpringBoot --spring.config.location=file:./application.yml $START_OPTS > $LOG_PATH 2>&1 &
log_debug "$SpringBoot SUCCESS..."
#echo -e "\033[32m $SpringBoot SUCCESS... \033[0m"
sleep 10s
tail -n 300 $LOG_PATH
}
function stop()
{
checkPid
log_info "Begin Stop Application $SpringBoot"
# echo "Begin Stop Application $SpringBoot"
if [ "$pid" -ne "0" ]; then
log_err "$SpringBoot stop..."
# echo "$SpringBoot stop..."
killPid
else
log_info "$SpringBoot not running!"
# echo "$SpringBoot not running!"
fi
}
function restart()
{
# stop
sleep 3s
start
}
function status()
{
checkPid
if [ "$pid" -ne "0" ]; then
log_info "$SpringBoot not running!"
#echo "$SpringBoot not running!"
else
log_info "$SpringBoot is running... PID:$pid"
# echo "$SpringBoot is running... PID:$pid"
fi
}
case $Purpose in
start) start;;
stop) stop;;
restart) restart;;
status) status;;
*) log_info "require start|stop|restart|status" ;;
esac
5. 使用
sh restart.sh $1 $2 $3
- $1 : SpringBoot 应用名.jar 必选
- $2 :操作内容,可空,为空默认为 restart
- $3 : 其他参数,可空
样例如下:
sh restart.sh cia-codegen.jar