一、背景介紹
項目開發時使用gradle
構建工具進行項目構建開發,藉助application
插件進行項目打包。
./gradlew clean build
執行以上命令後,gradle
會自動將項目打包爲zip
包,並放在build/distributions
目錄下。
該zip
包裏面包含了兩個目錄:
bin
:gradle
自動生成的啓動腳本,與項目/模塊名稱相同。lib
: 所有的運行時jar包。
一般情況下,使用gradle
自動生成的啓動腳本即可滿足需求,但要做成類似hadoop-daemon.sh
這樣的一鍵啓動腳本,仍需在自動生成的腳本基礎上做些封裝。
二、一鍵啓動腳本
封裝的一鍵啓動腳本主要擴展了以下功能:
- 通過
nohup
在後臺啓動服務,常駐運行。 - 捕獲服務的標準輸出,並保存到日誌。
- 自定義Log4j2的日誌文件名,添加用戶名、主機名。
- 設置JVM出現內存異常時的輸出日誌。
示例:
# !/usr/bin/env bash
#
# 服務一鍵啓動腳本
#
# @author chriscchen
# @createtime 2020-05-21
#
bin=`dirname ${BASH_SOURCE-$0}`
bin=`cd "$bin"; pwd`
# find correct directory
my_dir_prefix="/data/logs/data-studio"
# 模塊名稱,也是gradle自動生成的啓動腳本名稱
MODULE_NAME="ds-ide"
# 自定義日誌目錄
export DS_LOG_DIR=${my_dir_prefix}/${MODULE_NAME}
# 自定義Log4j2的日誌文件名,添加用戶名、主機名
LOG4J_OPTS="-Dlog.dir=${DS_LOG_DIR} -Dlog.file=${MODULE_NAME}-${USER}-${HOSTNAME}.log"
# 設置JVM出現內存異常時的輸出日誌
JVM_LOG_OPTS="-XX:HeapDumpPath=${DS_LOG_DIR} -XX:ErrorFile=${DS_LOG_DIR}/ps_err_pid%p.log"
# use JAVA_OPTS to pass the options
export JAVA_OPTS="${LOG4J_OPTS} ${JVM_LOG_OPTS}"
# 後臺啓動服務,並捕獲標準輸出,保存到日誌文件
nohup ${bin}/${MODULE_NAME} >> ${DS_LOG_DIR}/${MODULE_NAME}.out.$(date +%Y%m%d%H%M%S) 2>&1 &
if [[ $? != 0 ]]; then
echo "Failed to start ${MODULE_NAME}" 1>&2
exit 1
else
echo "${MODULE_NAME} started"
fi
三、一鍵停止腳本
一般情況下,停止進程都是通過kill
命令來完成,但kill
命令並不是同步的。
執行kill
命令,本質上只是向進程發送了“關閉”命令,進程接受到“關閉”命令並響應給kill
命令,這條命令就執行成功並退出了。
但進程是否已經停止,是由程序自身的停止邏輯決定。
所以參考 hadoop-daemon.sh stop
停止腳本,實現了一鍵停止服務腳本,主要實現了等待進程退出的功能。
示例:
# !/usr/bin/env bash
#
# @author chriscchen
# @createtime 2020-05-21
#
# 根據類名來查找進程。要注意的是:如果啓動進程時,顯式拼接了所有的jar包,會導致`ps`時顯示名稱過長,無法查找得類名的。一般在指定`classpath`時,通過`lib/*`的方式來指定。
PIDS=$(ps -ef | grep -v grep | grep "com.demo.Application" | awk '{print $2}')
SERVICE_NAME="ds-ide"
# 沒有找到進程,退出
if [[ -z "$PIDS" ]]; then
echo "No ${SERVICE_NAME} to stop" 1>&2
else
kill -s TERM ${PIDS}
echo "Waiting for ${SERVICE_NAME} process to exit"
# 通過 kill -0 來判斷進程是否已經退出
while kill -0 ${PIDS} 2>/dev/null; do
sleep 1
done
echo "Stopped ${SERVICE_NAME}"
fi