一個用于運行 Java SpringBoot 的 Linux 腳本 run.sh
- 自動根據(jù)所在目錄獲取最新的可執(zhí)行 jar盟迟、war
- 自動使用環(huán)境變量 JAVA_HOME 進行執(zhí)行命令(可配置)
- 支持優(yōu)雅下線(默認等待60秒秋泳,可配置,如果超過60秒則強制下線)
- 支持監(jiān)聽 SpringBoot 上線狀態(tài)
- 支持添加自定義運行參數(shù)(如:JVM 參數(shù)攒菠、SpringBoot 參數(shù))
- 支持常用功能:啟動迫皱、停止、重啟辖众、查看狀態(tài)卓起、查看日志
- SpringBoot 項目支持查看應用端口信息
目錄結(jié)構(gòu)如下
├── fastboot-0.0.1.jar # 應用jar
├── logs
│ ├── console.log # 應用控制臺輸入的日志
│ └── server.pid # 應用的pid
└── run.sh # 執(zhí)行腳本
可配置的參數(shù)
1. 用法
添加文件的執(zhí)行權(quán)限
# 首次執(zhí)行第一次即可
chmod +x run.sh
重啟并清空日志
查看狀態(tài)
啟動
停止
2. 說明
# 賦予執(zhí)行權(quán)限(初始化執(zhí)行一次)
chmod +x run.sh
# 啟動應用程序
sh run.sh start
# 停止應用程序
sh run.sh stop
# 查看應用程序
# sh run.sh status
# 重啟應用程序
sh run.sh restart c # 加 c 刪除歷史日志文件
# 查看應用日志
sh run.sh logs
3. 源碼(run.sh)
#!/bin/bash
#
# ================================================ 說明 start ================================================
# 1. 賦予執(zhí)行權(quán)限: chmod +x run.sh
# 2. 啟動應用程序: sh run.sh start
# 3. 停止應用程序: sh run.sh stop
# 4. 查看應用程序: sh run.sh status
# 5. 重啟應用程序: sh run.sh restart c # 加 c 刪除歷史日志文件
# 6. 查看應用日志: sh run.sh logs
# ================================================ 說明 end ================================================
#
# ================================================ 作者 start ================================================
# @author: houyu
# @date: 2021-08-19
# @mail: for.houyu@qq.com(272694308@qq.com)
# @blog: https://www.ihouyu.cn
# @csdn: https://blog.csdn.net/JinglongSource
# ================================================ 作者 end ================================================
# 刷新環(huán)境變量
source /etc/profile
# ================================================ 參數(shù) start ================================================
# ------ Java 路徑, 可以是 JAVA_HOME 也可以 Java 可執(zhí)行文件路徑, 默認使用環(huán)境變量的 JAVA_HOME
#JAVA="/usr/local/java/jdk1.8.0_281/bin/java"
#JAVA="/usr/local/java/jdk1.8.0_281"
JAVA="${JAVA_HOME}"
# ------ 文件路徑
DIR_PATH=`cd $(dirname $0); pwd`
# ------ 服務文件名(默認使用目錄下最新的.jar或者.war文件)
SERVER_FILE_NAME=`ls -t ${DIR_PATH} | egrep '\.jar|\.war' | head -1`
# ------ Java 參數(shù)
JAVA_OPT="${JAVA_OPT} -Duser.timezone=Asia/Shanghai"
JAVA_OPT="${JAVA_OPT} -Xms128m -Xmx128m"
# ------ Spring 參數(shù)
SPRING_OPT="${SPRING_OPT} --spring.profiles.active=prod"
# ------ 控制臺文件
#CONSOLE_FILE="/dev/null"
CONSOLE_FILE="${DIR_PATH}/logs/console.log"
# ------ 停止時等待超時的秒數(shù)
STOP_TIMEOUT=60
#
# ================================================ 參數(shù) end ================================================
#
# ================================================ 方法 start ================================================
#
# 服務PID
_SERVER_PID=0
# 服務端口
_SERVER_PORT=0
# 日志目錄
_LOG_PATH="${DIR_PATH}/logs"
# 輸入的第二個參數(shù)
_P2=$2
#
# =======================================
# 解析服務的 pid
# =======================================
resolve_server_pid() {
_SERVER_PID=0
if [ -f "${_LOG_PATH}/server.pid" ]; then
# pid 文件存在則讀取文件
_SERVER_PID=$(cat "${_LOG_PATH}/server.pid")
# 判斷是否真的存在這個 pid
if test $(ps -ef | awk '{print $2}' | grep -w ${_SERVER_PID} | wc -l) -eq 0; then
# 沒有找到文件記錄的 pid, 刪除這個文件
rm -rf ${_LOG_PATH}/server.pid
sleep 0.2
_SERVER_PID=0
# 重新解析服務的 pid
#resolve_server_pid
fi
#else
# # pid文件不存在則嘗試根據(jù)應用程序的名稱獲取pid
# if test $(pgrep -f ${SERVER_FILE_NAME} | wc -l) -gt 0; then
# # 說明應用程序正在跑, 使用第一個pid
# _SERVER_PID=$(pgrep -f ${SERVER_FILE_NAME} | head -1)
# else
# _SERVER_PID=0
# fi
fi
}
#
# =======================================
# 解析服務的端口
# =======================================
retry_resolve_server_port() {
_SERVER_PORT=0
# 解析服務的 pid
resolve_server_pid
# 檢測5分鐘(300秒)
for(( i=0; i<=300; i++ )); do
# 判斷 pid 是否存在
if test $(ps -ef | awk '{print $2}' | grep -w ${_SERVER_PID} | wc -l) -eq 0; then
rm -rf ${_LOG_PATH}/server.pid
_SERVER_PID=0
break
fi
# 判斷 pid 的 port 是否綁定
if test $(netstat -tulnp | grep "${_SERVER_PID}/" | wc -l) -gt 0; then
_SERVER_PORT=$(netstat -tulnp | grep "${_SERVER_PID}/" | head -1 | awk '{print $4}' | awk -F ":" '{print $NF}')
break
fi
[[ $i%5 -eq 0 ]] && echo "--- Observing server port using ${i}s"
sleep 1
done
}
#
# =======================================
# 啟動
# =======================================
start() {
echo "----------------------------------------------------------------------------- Start [0] ------"
# 解析服務的 pid
resolve_server_pid
if [ "${_SERVER_PID}" -ne 0 ]; then
# 如果 _SERVER_PID != 0, 那就說明應用程序在運行,不進行啟動
echo "--- Do not start, ${SERVER_FILE_NAME} already started! [ SERVER_PID = ${_SERVER_PID} ]"
echo "----------------------------------------------------------------------------- Start [1] ------"
return
fi
#
echo "--- Starting ${SERVER_FILE_NAME} ..."
# 如果 JAVA 是一個目錄,則加上/bin/java
[ -d "${JAVA}" ] && JAVA="${JAVA}/bin/java"
# 如果 JAVA 不是一個存在的文件, 則嘗試使用默認的java
[ ! -e "$JAVA" ] && JAVA=`which java`
# 最終 Java 不是一個存在的文件, 則清空
[ ! -e "$JAVA" ] && unset JAVA
if [ -z "${JAVA}" ]; then
echo -e "\033[31mPlease set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better!\033[0m"
exit 1
fi
# 判斷是否需要刪除日志文件
if [[ "$_P2" == "c" ]]; then
# 如果第二個參數(shù)是c, 刪除日志文件
echo -e "--- \033[33mDeleting ${SERVER_FILE_NAME} log file(${DIR_PATH}/logs/*)\033[0m"
rm -rf ${DIR_PATH}/logs/*
fi
# 檢查日志目錄
test -d ${DIR_PATH}/logs/ || mkdir -p ${DIR_PATH}/logs/
# 執(zhí)行腳本
# nohup java -Xmx128m -jar /home/app/server.jar --spring.profiles.active=prod >> /home/app/logs/console.log 2>&1 & echo $! > /home/app/logs/server.pid
#
# 運行的參數(shù)
RUN_OPT="${JAVA_OPT} -jar"
RUN_OPT="${RUN_OPT} ${DIR_PATH}/${SERVER_FILE_NAME}"
[ -n "${SPRING_OPT}" ] && RUN_OPT="${RUN_OPT} ${SPRING_OPT}"
#
echo "--- $JAVA ${RUN_OPT}"
echo "$JAVA ${RUN_OPT}" > ${CONSOLE_FILE}
nohup $JAVA ${RUN_OPT} >> ${CONSOLE_FILE} 2>&1 & echo $! > ${_LOG_PATH}/server.pid
#
echo "--- ${SERVER_FILE_NAME} is running"
echo -e "--- You can check the log file \033[36m${CONSOLE_FILE}\033[0m or execute the command on the next line"
echo -e "--- \033[40;37mtail -f -n 300 ${CONSOLE_FILE}\033[0m"
# 睡眠1秒
sleep 1
# 解析服務的 pid
resolve_server_pid
# 判斷應用程序是否成功跑起來
if [ "${_SERVER_PID}" -eq 0 ]; then
echo -e "--- \033[31mStart failure ${SERVER_FILE_NAME}\033[0m"
else
if [ -n "${SPRING_OPT}" ]; then
# 如果是 Spring 項目, 需要觀察端口是否成功綁定
retry_resolve_server_port
#
if [ "${_SERVER_PORT}" -eq 0 ]; then
echo -e "--- \033[31mStart failure ${SERVER_FILE_NAME}\033[0m"
else
_SERVER_PORT_TEXT=`netstat -tulnp | grep "${_SERVER_PID}/" | awk 'BEGIN{ORS=" "}{print $4}'`
echo -e "--- \033[32mStart successfully ${SERVER_FILE_NAME} [ SERVER_PID = ${_SERVER_PID}, SERVER_PORT = ${_SERVER_PORT_TEXT}]\033[0m"
fi
else
echo -e "--- \033[32mStart successfully ${SERVER_FILE_NAME} [ SERVER_PID = ${_SERVER_PID}, \033[0m\033[33mSERVER_PORT = unknown \033[0m\033[32m]\033[0m"
fi
fi
echo "----------------------------------------------------------------------------- Start [1] ------"
}
#
# =======================================
# 停止
# =======================================
stop() {
echo "----------------------------------------------------------------------------- Stop [0] ------"
# 解析服務的 pid
resolve_server_pid
if [ "${_SERVER_PID}" -eq 0 ]; then
# _SERVER_PID = 0, 那就說明應用程序沒有在運行
echo "--- Stopped ${SERVER_FILE_NAME}"
echo "----------------------------------------------------------------------------- Stop [1] ------"
return
fi
echo "--- Graceful Stopping ${SERVER_FILE_NAME} ..."
# 進行優(yōu)雅停機
kill -15 ${_SERVER_PID}
# 當前等待秒數(shù)
_wait_seconds=0
#
while true; do
if test $(ps -ef | awk '{print $2}' | grep -w ${_SERVER_PID} | wc -l) -eq 0; then
echo "--- Stopped ${SERVER_FILE_NAME}"
break
fi
if [ "${_wait_seconds}" -ge "${STOP_TIMEOUT}" ]; then
# 強制終止進程
echo -e "--- \033[31mWait timeout(${_wait_seconds}s), forced shutdown [kill -9 ${SERVER_FILE_NAME}].\033[0m"
sudo kill -9 ${_SERVER_PID}
break
fi
sleep 1
let _wait_seconds++
echo "--- Wait ${_wait_seconds}s"
done
if [ $? -eq 0 ]; then
echo "--- Stop successfully ${SERVER_FILE_NAME}"
rm -rf ${_LOG_PATH}/server.pid
else
echo "--- Stop failure ${SERVER_FILE_NAME}"
fi
echo "----------------------------------------------------------------------------- Stop [1] ------"
}
#
# =======================================
# 狀態(tài)
# =======================================
status() {
echo "----------------------------------------------------------------------------- status [0] ------"
# 解析服務的 pid
resolve_server_pid
if [ "${_SERVER_PID}" -eq 0 ]; then
echo -e "--- \033[33mStopped $SERVER_FILE_NAME\033[0m"
else
_SERVER_PORT_TEXT=`netstat -tulnp | grep "${_SERVER_PID}/" | awk 'BEGIN{ORS=" "}{print $4}'`
[ -z "$_SERVER_PORT_TEXT" ] && _SERVER_PORT_TEXT="unknown "
echo -e "--- \033[32mRunning ${SERVER_FILE_NAME} [ SERVER_PID = ${_SERVER_PID}, SERVER_PORT = ${_SERVER_PORT_TEXT}]\033[0m"
echo -e "--- You can check the log file \033[36m${CONSOLE_FILE}\033[0m or execute the command on the next line"
echo -e "--- \033[40;37mtail -f -n 300 ${CONSOLE_FILE}\033[0m"
fi
echo "----------------------------------------------------------------------------- status [1] ------"
}
#
# =======================================
# 日志
# =======================================
logs() {
tail -f -n 3000 ${CONSOLE_FILE}
}
#
# ================================================ 方法 end ================================================
#
case "$1" in
'start')
start
;;
'stop')
stop
;;
'restart')
stop
start
;;
'status')
status
;;
'logs')
logs
;;
*)
echo "requires parameter [start|stop|restart|status|logs] [c]?"
exit 1
;;
esac
#
#
#
- 公眾號:IT加載中(it_loading)
- CSDN:https://blog.csdn.net/JinglongSource
- 博客:https://ihouyu.cn/
- 郵箱:for.houyu@qq.com