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

319 lines
9.5 KiB
Bash

#!/bin/bash
# 通用启动脚本, 如果需要自定义, 可在 `${project.basedir}/src/main/resources/bin` 目录下创建同名文件, 框架在打包时将会使用自定义脚本.
# ____
# /\ _`\ __ /'\_/`\
# \ \ \L\ \ __ __ ___ ___ /\_\ ___ __ /\ \ __ ___
# \ \ , / /\ \/\ \ /' _ `\ /' _ `\ \/\ \ /' _ `\ /'_ `\ \ \ \__\ \ /'__`\ /' _ `\
# \ \ \\ \ \ \ \_\ \/\ \/\ \ /\ \/\ \ \ \ \ /\ \/\ \ /\ \L\ \ \ \ \_/\ \ /\ \L\.\_ /\ \/\ \
# \ \_\ \_\ \ \____/\ \_\ \_\\ \_\ \_\ \ \_\\ \_\ \_\\ \____ \ \ \_\\ \_\\ \__/.\_\\ \_\ \_\
# \/_/\/ / \/___/ \/_/\/_/ \/_/\/_/ \/_/ \/_/\/_/ \/___L\ \ \/_/ \/_/ \/__/\/_/ \/_/\/_/
# /\____/
#
# :: FKH Boot Startup Script ::
readonly TURN=0 && readonly FALSE=1
# 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() {
if [[ "${DEBUG_PORD}" != "-1" ]]; then
expr "${DEBUG_PORD}" "+" 10 &>/dev/null
if [[ $? -eq 0 ]]; 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
}
# 获取应用部署路径
function prepare() {
APP_HOME=$(pwd)
dirname $0 | grep "^/" >/dev/null
if [[ $? -eq 0 ]]; then
APP_HOME=$(dirname $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
# 默认使用打包后的 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
}
# 获取 pid
function check_pid() {
PID=$(ps -ef | grep -v grep | grep "${APP_NAME}@${ENV}" | awk '{print $2}')
}
# 创建日志目录和文件
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() {
# 设置 JVM 参数
local jvm_opts="-Dname=${APP_NAME} -Xloggc:${GC_LOG} -XX:ErrorFile=${DEPLOY_DIR}/app_error_%p.log -XX:HeapDumpPath=${DEPLOY_DIR}/app_error.hprof"
jvm_opts="${jvm_opts} #{jvmOptions}"
echo -e "JVM 启动参数: ${jvm_opts}"
nohup ${javaexe} -jar \
${jvm_opts} \
-Dloader.home=${DEPLOY_DIR}/ \
-Dloader.path=lib/ \
-DAPP_NAME=${APP_NAME} \
-DIDENTIFY=${APP_NAME}@${ENV} \
-DFKH_NAME_SPACE=fkh-${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} \
${DEBUG_OPTS} \
${JAR_FILE} \
--slot.root=${DEPLOY_DIR}/ \
--slot.path=patch/ \
--slot.path=plugin/ \
--spring.profiles.active=${ENV} \
--spring.config.location=${DEPLOY_DIR}/config/ >${FINAL_LOG_PATH}/${LOG_NAME} 2>&1 &
echo_green "--------------------------------------"
echo_green " starting ${APP_NAME} "
echo_green "--------------------------------------"
}
# 输出所有参数
function show_info() {
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[35m DEBUG_OPTS: ${DEBUG_OPTS} \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"
}
# 启动应用 (-s env)
function start() {
check_pid
if [[ -z "${PID}" ]]; then
mkdir_log_file
running
echo_green "日志路径: ${FINAL_LOG_PATH}/${LOG_NAME}"
echo -e "\n"
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'
fi
else
echo_green "${APP_NAME}@${ENV} is running. [pid: ${PID}]"
fi
}
# 关闭应用 (-S env)
function stop() {
check_pid
if [[ ! -n "${PID}" ]]; then
echo_red "The ${APP_NAME}@${ENV} does not started!"
else
echo_red "shudown the ${APP_NAME}@${ENV}"
kill ${PID} >/dev/null 2>&1
COUNT=0
KILL_COUNT=0
# COUNT 小于 1
while [[ ${COUNT} -lt 1 ]]; do
echo -e ".\c"
KILL_COUNT=$((${KILL_COUNT} + 1))
if [[ ${KILL_COUNT} -gt 5 ]]; then
echo -e "\n"
kill -9 ${PID} >/dev/null 2>&1
fi
# 检查是否干掉 app
PID_EXIST=$(ps -ef | grep -v grep | grep "${APP_NAME}@${ENV}" | awk '{print $2}')
# 如果为空, 则退出循环
if [[ ! -n "${PID_EXIST}" ]]; then
COUNT=1
fi
sleep 1s
done
echo
echo_green "${APP_NAME}@${ENV} shudown success. [pid = ${PID}]"
fi
}
# 重启应用 (-r env)
function restart() {
stop
sleep 1s
start
}
# 查看应用状态 (-c env)
function status() {
check_pid
if [[ ! -n "${PID}" ]]; then
echo_green "${APP_NAME}@${ENV} not running"
else
echo_red "${APP_NAME}@${ENV} running. [pid: ${PID}]"
fi
}
JAVA_HOME=$(echo ${JAVA_HOME})
if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
javaexe="$JAVA_HOME/bin/java"
elif type -p java >/dev/null 2>&1; then
javaexe=$(type -p java)
elif [[ -x "/usr/bin/java" ]]; then
javaexe="/usr/bin/java"
else
# 测试, 预演 jdk 的绝对路径
javaexe="$(find /opt -name 'jdk1.8*')/bin/java"
fi
echo "JAVA_HOME: ${javaexe}"
# 默认 dev 环境
ENV="dev"
# 默认为启动
FUNC="start"
# 默认 debug 关闭
DEBUG_PORD="-1"
# 默认启动后 tail 日志
SHOW_LOG="off"
SHOW_INFO="off"
# shell 启动时使用默认的日志路径
LOG_PATH="/mnt/syslogs/v5"
LOG_NAME="all.log"
# 最终的日志路径
FINAL_LOG_PATH=""
# 自定义参数, 先设置变量
while getopts "s:r:S:d:c:h:tTiH" 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}
;;
t)
SHOW_LOG="on"
;;
T)
SHOW_LOG="on"
LOG_PATH=$(mktemp -d)
;;
h)
ENV=${OPTARG}
FUNC="helper"
;;
i)
SHOW_INFO="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 全量日志)
-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(启动时输出参数信息)
\033[0m"
exit 1
;;
\?)
echo_red "参数列表错误 使用 -H 查看帮助"
exit 1
;;
esac
done
# 1. 处理 debug 参数
init_debug
# 2. 处理部署包路径
prepare
# 3. 通过 func 来执行具体方法
case ${FUNC} in
start) start ;;
stop) stop ;;
restart) restart ;;
status) status ;;
*) echo_red "参数错误 require -s|-r|-S|-c" ;;
esac