Files
2022-06-13 12:38:00 +08:00

465 lines
15 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 通用启动脚本, 如果需要自定义, 可在 `${project.basedir}/src/main/resources/bin` 目录下创建同名文件, 框架在打包时将会使用自定义脚本.
# ____
# /\ _`\ __ /'\_/`\
# \ \ \L\ \ __ __ ___ ___ /\_\ ___ __ /\ \ __ ___
# \ \ , / /\ \/\ \ /' _ `\ /' _ `\ \/\ \ /' _ `\ /'_ `\ \ \ \__\ \ /'__`\ /' _ `\
# \ \ \\ \ \ \ \_\ \/\ \/\ \ /\ \/\ \ \ \ \ /\ \/\ \ /\ \L\ \ \ \ \_/\ \ /\ \L\.\_ /\ \/\ \
# \ \_\ \_\ \ \____/\ \_\ \_\\ \_\ \_\ \ \_\\ \_\ \_\\ \____ \ \ \_\\ \_\\ \__/.\_\\ \_\ \_\
# \/_/\/ / \/___/ \/_/\/_/ \/_/\/_/ \/_/ \/_/\/_/ \/___L\ \ \/_/ \/_/ \/__/\/_/ \/_/\/_/
# /\____/
#
# :: FKH Boot Startup Script ::
# ANSI Colors
echo_red() { echo $'\e[0;31m'"$1"$'\e[0m'; }
echo_green() { echo $'\e[0;32m'"$1"$'\e[0m'; }
echo_yellow() { echo $'\e[0;33m'"$1"$'\e[0m'; }
# info级别的日志
log_info() {
echo -e "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: \033[32m [info] \033[0m $*" >&2
}
# error级别的日志
log_error() {
echo -e "[$(date +'%Y-%m-%dT%H:%M:%S%z')][$$]: \033[31m [error] \033[0m $*" >&2
}
# 初始化 debug 参数
function init_debug() {
DEBUG_OPTS="-Dloader.debug=false"
if [[ "${DEBUG_PORD}" != "-1" ]]; then
if (("${DEBUG_PORD}" + 10)) &>/dev/null; then
DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$DEBUG_PORD -Dloader.debug"
echo_yellow "开启 DEBUG 模式: DEBUG_OPTS=$DEBUG_OPTS"
else
echo_red "请输入正确的端口号"
exit 1
fi
fi
}
# 初始化 JMX 参数
function init_jmx() {
JMX_OPTIONS="-Dcom.sun.management.jmxremote=false"
if [[ "${JMX_PORD}" != "-1" ]]; then
if (("${JMX_PORD}" + 10)) &>/dev/null; then
local local_ip
local_ip=$(ifconfig -a | grep inet | grep -v 127.0.0.1 | grep -v inet6 | awk '{print $2}' | sed -e 's/addr://g')
JMX_OPTIONS="-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=$JMX_PORD
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.rmi.server.hostname=${local_ip}"
echo_yellow "开启 JMX 模式: JMX_OPTIONS=${JMX_OPTIONS}"
else
echo_red "请输入正确的端口号"
exit 1
fi
fi
}
# 获取应用部署路径
function prepare() {
local app_home
app_home="$(pwd)"
if dirname "$0" | grep "^/" >/dev/null; then
app_home=$(dirname "$0")
else
dirname "$0" | grep "^\." >/dev/null
local 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
# 默认使用打包后的 artifactId 作为应用名
DEPLOY_DIR=$(dirname "$app_home")
# 从 build-info.properties 读取应用名
APP_NAME=$(awk -F '=' '{if($1~/build.project.name/) printf $2}' "${DEPLOY_DIR}"/config/build-info.properties)
JAR_FILE=${DEPLOY_DIR}/${APP_NAME}.jar
GC_LOG=${LOG_PATH}/${ENV}/${APP_NAME}/gc.log
}
# 处理 apm 参数
function init_apm() {
if [[ ${ENABLE_APM} = "on" ]]; then
APM_OPTS="-javaagent:/opt/skywalking/agent/skywalking-agent.jar
-Dskywalking.agent.service_name=${APP_NAME}@${ENV}"
echo_yellow "开启 APM 模式: APM_OPTS=$APM_OPTS"
fi
}
# 获取 pid
function check_pid() {
local identify
identify=${APP_NAME}@${ENV}
echo $(pgrep -f $identify)
}
# 创建日志目录和文件
function mkdir_log_file() {
FINAL_LOG_PATH="${LOG_PATH}/${ENV}/${APP_NAME}"
mkdir -p "${FINAL_LOG_PATH}"
if [[ ! -f "${FINAL_LOG_PATH}/${LOG_NAME}" ]]; then
touch "${FINAL_LOG_PATH}/${LOG_NAME}"
fi
}
# 设置环境, 与应用配置 bootstrap.yml 的 ${FKH_NAME_SPACE} 对应, 此处设置的变量会被 spring 在启动时替换
# 使用 -DIDENTIFY 来区分应用 (由于服务器资源有限, 一台服务器可能会部署同一个应用, 只是环境不同而已)
# 重写 fkh.logging.file.path 配置
function running() {
echo -e "JVM 启动参数: ${JVM_OPTIONS}"
echo
echo_green "启动命令:
nohup ${JAVA_EXE} -jar
-Djava.security.egd=file:/dev/./urandom
${JVM_OPTIONS}
-Xloggc:${GC_LOG}
-XX:ErrorFile=${DEPLOY_DIR}/app_error_%p.log
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=${DEPLOY_DIR}/app_error.hprof
-XX:OnOutOfMemoryError='kill -9 %p'
-Dloader.home=${DEPLOY_DIR}/
-Dloader.path=lib/
-DAPP_NAME=${APP_NAME}
-DIDENTIFY=${APP_NAME}@${ENV}
-DFKH_NAME_SPACE=${NAME_SPACE_PREFIX}-${ENV}
-Ddeploy.path=${DEPLOY_DIR}
-Dstart.type=shell
-Dconfig.path=${DEPLOY_DIR}/config/
-Dfkh.logging.file.path=${LOG_PATH}/${ENV}
-Dfkh.logging.file.name=${LOG_NAME}
-Djar.file=${JAR_FILE}
${JMX_OPTIONS}
${DEBUG_OPTS}
${APM_OPTS}
${JAR_FILE}
--spring.profiles.active=${ENV}
--spring.config.location=${DEPLOY_DIR}/config/
--slot.root=${DEPLOY_DIR}/
--slot.path=patch/
--slot.path=plugin/ >${FINAL_LOG_PATH}/${LOG_NAME} 2>&1 &"
nohup "$JAVA_EXE" -jar \
-Djava.security.egd=file:/dev/./urandom \
${JVM_OPTIONS} \
-Xloggc:"$GC_LOG" \
-XX:ErrorFile="$DEPLOY_DIR"/app_error_%p.log \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath="$DEPLOY_DIR"/app_error.hprof \
-XX:OnOutOfMemoryError='kill -9 %p' \
-Dloader.home="$DEPLOY_DIR"/ \
-Dloader.path=lib/ \
-DAPP_NAME="$APP_NAME" \
-DIDENTIFY="$APP_NAME"@"$ENV" \
-DFKH_NAME_SPACE="$NAME_SPACE_PREFIX"-"$ENV" \
-Ddeploy.path="$DEPLOY_DIR" \
-Dstart.type=shell \
-Dconfig.path="$DEPLOY_DIR"/config/ \
-Dfkh.logging.file.path="$LOG_PATH"/"$ENV" \
-Dfkh.logging.file.name="$LOG_NAME" \
-Djar.file="$JAR_FILE" \
${JMX_OPTIONS} \
${DEBUG_OPTS} \
${APM_OPTS} \
"$JAR_FILE" \
--spring.profiles.active="$ENV" \
--spring.config.location="$DEPLOY_DIR"/config/ \
--slot.root="$DEPLOY_DIR"/ \
--slot.path=patch/ \
--slot.path=plugin/ >"$FINAL_LOG_PATH"/"$LOG_NAME" 2>&1 &
echo
echo_green "----------------------------------------------------------------------------"
echo_green " 启动应用: ${APP_NAME} ${ENV} "
echo_green " 日志路径: ${FINAL_LOG_PATH}/${LOG_NAME}"
echo_green "----------------------------------------------------------------------------"
}
# 输出所有参数
function show_info() {
echo
echo -e "\033[31m ENV: ${ENV} \033[0m"
echo -e "\033[32m FUNC: ${FUNC} \033[0m"
echo -e "\033[33m DEBUG_PORD: ${DEBUG_PORD} \033[0m"
echo -e "\033[34m SHOW_LOG: ${SHOW_LOG} \033[0m"
echo -e "\033[34m TIMEOUT_SHOWLOG: ${TIMEOUT_SHOWLOG} \033[0m"
echo -e "\033[35m DEBUG_OPTS: ${DEBUG_OPTS} \033[0m"
echo -e "\033[35m JMX_OPTIONS: ${JMX_OPTIONS} \033[0m"
echo -e "\033[36m APP_NAME: ${APP_NAME} \033[0m"
echo -e "\033[31m DEPLOY_DIR: ${DEPLOY_DIR} \033[0m"
echo -e "\033[32m JAR_FILE: ${JAR_FILE} \033[0m"
echo -e "\033[34m GC_LOG: ${GC_LOG} \033[0m"
echo -e "\033[35m FINAL_LOG_PATH: ${FINAL_LOG_PATH} \033[0m"
echo -e "\033[36m JAVA_HOME: ${JAVA_HOME} \033[0m"
echo -e "\033[32m IDENTIFY: ${APP_NAME}@${ENV} \033[0m"
echo -e "\033[33m FKH_NAME_SPACE: fkh-${ENV} \033[0m"
echo -e "\n"
}
# 启动应用 (-s env)
function start() {
echo
echo_green "invoke start()"
local pid
pid="$(check_pid)"
if [[ -z "$pid" ]]; then
mkdir_log_file
running
if [[ ${SHOW_INFO} = "on" ]]; then
show_info
fi
if [[ ${SHOW_LOG} = "on" ]]; then
tail -100f "${FINAL_LOG_PATH}"/"${LOG_NAME}" | perl -pe 's/(WARN)|(ERROR)/\e[1;33m$1\e[0m\e[1;31m$2\e[0m/g'
else
if [[ ${TIMEOUT_SHOWLOG} = "on" ]]; then
timeout 120 tail -100f "${FINAL_LOG_PATH}"/"${LOG_NAME}" | perl -pe 's/(WARN)|(ERROR)/\e[1;33m$1\e[0m\e[1;31m$2\e[0m/g'
fi
fi
else
echo_green "${APP_NAME}@${ENV} is running. [pid: $pid]"
fi
}
# 关闭应用 (-S env)
function stop() {
echo
echo_green "invoke stop()"
local pid
pid="$(check_pid)"
if [[ -z "${pid}" ]]; then
echo_red "The ${APP_NAME}@${ENV} does not started!"
else
local current_pid=${pid}
echo_red "shudown the ${APP_NAME}@${ENV} [pid: ${current_pid}]"
kill "${pid}" >/dev/null 2>&1
local count=0
local kill_count=0
# count 小于 1
while [[ ${count} -lt 1 ]]; do
echo -e ".\c"
((kill_count++))
if [[ ${kill_count} -gt 5 ]]; then
echo -e "\n"
kill -9 "${pid}" >/dev/null 2>&1
fi
# 检查是否干掉 app
pid="$(check_pid)"
# 如果为空, 则退出循环
if [[ -z "${pid}" ]]; then
count=1
fi
sleep 1s
done
echo
echo_green "${APP_NAME}@${ENV} shudown success [pid: ${current_pid}]"
fi
}
# 重启应用 (-r env)
function restart() {
echo
echo_green "invoke restart()"
stop
sleep 1s
start
}
# 查看应用状态 (-c env)
function status() {
echo
echo_green "invoke status()"
local pid
pid="$(check_pid)"
if [[ -z "${pid}" ]]; then
echo_red "${APP_NAME}@${ENV} not running!"
else
echo_green "${APP_NAME}@${ENV} running. [pid: ${pid}]"
fi
}
############################################################
# 从这里开始执行 #
############################################################
echo
echo_green " ___________ ____ __. ___ ___ .__ .__ "
echo_green " \_ _____/| |/ _| / | \ | | _____ __ __ ____ ____ | |__ ____ _______ "
echo_green " | __) | < / ~ \ ______ | | \__ \ | | \ / \ _/ ___\ | | \ _/ __ \ \_ __ \ "
echo_green " | \ | | \ \ Y / /_____/ | |__ / __ \_| | /| | \\ \___ | Y \\ ___/ | | \/ "
echo_green " \___ / |____|__ \ \___|_ / |____/(____ /|____/ |___| / \___ >|___| / \___ > |__| "
echo_green " \/ \/ \/ \/ \/ \/ \/ \/ "
echo_green " "
echo_green " :: FKH Boot Startup Script :: "
echo
if [[ -n "${JAVA_HOME}" ]] && [[ -x "${JAVA_HOME}/bin/java" ]]; then
JAVA_EXE="${JAVA_HOME}/bin/java"
elif type -p java >/dev/null 2>&1; then
JAVA_EXE=$(type -p java)
elif [[ -x "/usr/bin/java" ]]; then
JAVA_EXE="/usr/bin/java"
else
# 测试, 预演 jdk 的绝对路径
JAVA_EXE="$(find /opt -name 'jdk1.8*')/bin/java"
fi
echo_green "JAVA_HOME: ${JAVA_EXE}"
# 默认 dev 环境
ENV="prod"
NAME_SPACE_PREFIX="fkh"
# 默认为启动
FUNC="restart"
# 默认 debug 关闭
DEBUG_PORD="-1"
JMX_PORD="-1"
# 默认启动后 tail 日志
SHOW_LOG="off"
TIMEOUT_SHOWLOG="off"
SHOW_INFO="on"
ENABLE_APM="off"
# shell 启动时使用默认的日志路径
LOG_PATH="/mnt/syslogs/v5"
LOG_NAME="all.log"
# 最终的日志路径
FINAL_LOG_PATH=""
# 自定义 JVM 参数
JVM_OPTIONS="#{jvmOptions}"
# 自定义参数, 先设置变量
while getopts "s:r:S:d:m:c:n:h:o:tqTiwH" opt; do
case ${opt} in
# 启动应用, 跟环境变量
s)
ENV=${OPTARG}
FUNC="start"
;;
# 重启应用 跟环境变量
r)
ENV=${OPTARG}
FUNC="restart"
;;
c)
ENV=${OPTARG}
FUNC="status"
;;
# 关闭应用
S)
ENV=${OPTARG}
FUNC="stop"
;;
# 使用 debug 模式 跟监听端口
d)
DEBUG_PORD=${OPTARG}
;;
m)
JMX_PORD=${OPTARG}
;;
n)
NAME_SPACE_PREFIX=${OPTARG}
;;
t)
SHOW_LOG="on"
;;
q)
TIMEOUT_SHOWLOG="on"
;;
T)
SHOW_LOG="on"
LOG_PATH=$(mktemp -d)
;;
h)
ENV=${OPTARG}
FUNC="helper"
;;
o)
JVM_OPTIONS=${OPTARG}
;;
i)
SHOW_INFO="on"
;;
w)
ENABLE_APM="on"
;;
H)
echo -e "\033[0;36m
1. 在任何目录下执行此脚本都可以.
2. 最简单的命令就是不输入任何参数 (./server.sh 即以${ENV}环境启动应用).
3. 输入 -s , -r 和 -S 参数, 后面必须跟环境变量 (dev/test/prod);
4. -d, -t, -T -i 参数不能单独存在, 且必须跟在 -s 或者 -r 后面;
5. -h 参数不需要手动调用, 是程序自动调用, 用于启动 helper 进程来对主应用进行重启操作.\033[0m \033[0;34m
-s:启动应用 exp: bin/server.sh (默认以 ${ENV} 环境启动应用, 不需要输入任何参数)
-s:启动应用 exp: bin/server.sh -s test (以 test 环境启动应用)
-r:重启应用 exp: bin/server.sh -r prod (以 prod 环境重启应用)
-S:关闭应用 exp: bin/server.sh -S test (关闭应用)
-c:查看状态 exp: bin/server.sh -c test (查看应用状态)
-t:查看日志 exp: bin/server.sh -s dev (启动时自动 tail 全量日志)
-q:查看日志(0秒超时时间)exp:bin/server.sh -s dev -q (-t和-q全是查看日志,一个有超时时间,一个没有,二择其一就可以,-t优先级要高)
-T:输出日志到临时文件\texp: bin/server.sh -s dev -T\t\t(启动时自动 tail 全量日志, 主要用于部署测试用, 日志会写入到临时文件, 生产环境不要使用)
-d:debug 模式\t\texp: bin/server.sh -s dev -d 5005\t(以 dev 环境启动应用, 端口默认为 5005, 并且开启 debug 模式, 用于 idea 远程调试)
-i:输出所有参数信息\texp: bin/server.sh -s dev -i\t\t(启动时输出参数信息)
-w:启用 APM\texp: bin/server.sh -s dev -w\t\t(启动时输出参数信息)
-m:启用 JMX 远程监控\texp: bin/server.sh -s dev -m 10089\t\t(需要添加监听端口)
-o:重写 JVM 配置\texp: bin/server.sh -s dev -o '-Xms256M -Xmx512M'\t\t(启动时输出参数信息)
-n:重写 FKH_NAME_SAPCE 参数\texp: bin/server.sh -s dev -n 'fkh'\t\t(重写的前缀, 最终为 {prefix}-{ENV}, 请不要添加 "-")
\033[0m"
exit 1
;;
\?)
echo_red "参数列表错误 使用 -H 查看帮助"
exit 1
;;
esac
done
echo
echo_green "--------------------------------------"
echo_green " 1. 处理 debug 参数 "
echo_green "--------------------------------------"
init_debug
echo
echo_green "--------------------------------------"
echo_green " 2. 处理 JMX 参数 "
echo_green "--------------------------------------"
init_jmx
echo
echo_green "--------------------------------------"
echo_green " 3. 处理部署相关参数 "
echo_green "--------------------------------------"
prepare
echo
echo_green "--------------------------------------"
echo_green " 4. 初始化 APM 参数 "
echo_green "--------------------------------------"
init_apm
# 通过 func 来执行具体方法
case ${FUNC} in
start) start ;;
stop) stop ;;
restart) restart ;;
status) status ;;
*) echo_red "参数错误 require -s|-r|-S|-c" ;;
esac