一、背景介绍
项目开发时使用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