linux自动建立软引用,spring boot在linux部署到/etc/init.d/app.jar 实现原理揭秘

从spring boot的文档知道,spring boot打包一个可以在systemV,直接执行的jar文件。操作也很简单,只需要在pom.xml中加入

org.springframework.boot

spring-boot-maven-plugin

true

通过配置后, 进行打包,生成的jar文件就可以放到 /etc/init.d/ 里面(ln -s 的软引用也可以)。这样配置之后java应用, 就可以和mysql等等的服务一样使用/etc/init.d/app.jar {start|stop|status|restart|force-reload}  进行操作了。

我们来探究一下,它是如何实现的

通过查阅资料systemV目录 /etc/init.d/ 中的文件得知, /etc/init.d/中全部是shell脚本,但是我们的app.jar是一个jar文件,对jar文件的认识它的真实格式是zip压缩文件, 这个jar有什么魔力呢?把app.jar,拖入到jd-gui.exe , 看看它到底长什么样,结果报错说文件是无效的,这明明就是个jar文件, 为什么会这样, 难道是jd-gui.exe的bug ?尝试使用360压缩打开这个app.jar, 成功打开, 看到里面有一些spring boot引导启动的class和自己写的class以及第三方库的class文件,但是它到底怎么实现的原理还没找到

最后我们把jar文件拖入 winhex (一个文件二进制查看器)

ca04f473881245f42c64ce14ec39c385.png

我们可以看到,这个文件开头是 #!/bin/bash ,那么它是一个bash脚本文件,继续往下看

6a4aec2fbdd90608bc530c81738edbce.png

看到pk 这是zip格式的开头,到这里明白了, 这个jar文件是一个 bash shell和 jar文件的合体文件, spring boot 打包时候把 /etc/init.d/用到脚本加在了文件前面,这个实现方法确实巧妙。

来看看这个脚本的真容

#!/bin/bash

#

# . ____ _ __ _ _

# /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \

# ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \

# \\/ ___)| |_)| | | | | || (_| | ) ) ) )

# ' |____| .__|_| |_|_| |_\__, | / / / /

# =========|_|==============|___/=/_/_/_/

# :: Spring Boot Startup Script ::

#

### BEGIN INIT INFO

# Provides: xxxx

# Required-Start: $remote_fs $syslog $network

# Required-Stop: $remote_fs $syslog $network

# Default-Start: 2 3 4 5

# Default-Stop: 0 1 6

# Short-Description: xxxx

# Description: log collect system

# chkconfig: 2345 99 01

### END INIT INFO

[[ -n "$DEBUG" ]] && set -x

# Initialize variables that cannot be provided by a .conf file

WORKING_DIR="$(pwd)"

# shellcheck disable=SC2153

[[ -n "$JARFILE" ]] && jarfile="$JARFILE"

[[ -n "$APP_NAME" ]] && identity="$APP_NAME"

# Follow symlinks to find the real jar and detect init.d script

cd "$(dirname "$0")" || exit 1

[[ -z "$jarfile" ]] && jarfile=$(pwd)/$(basename "$0")

while [[ -L "$jarfile" ]]; do

if [[ "$jarfile" =~ init\.d ]]; then

init_script=$(basename "$jarfile")

else

configfile="${jarfile%.*}.conf"

# shellcheck source=/dev/null

[[ -r ${configfile} ]] && source "${configfile}"

fi

jarfile=$(readlink "$jarfile")

cd "$(dirname "$jarfile")" || exit 1

jarfile=$(pwd)/$(basename "$jarfile")

done

jarfolder="$( (cd "$(dirname "$jarfile")" && pwd -P) )"

cd "$WORKING_DIR" || exit 1

# Inline script specified in build properties

# Source any config file

configfile="$(basename "${jarfile%.*}.conf")"

# Initialize CONF_FOLDER location defaulting to jarfolder

[[ -z "$CONF_FOLDER" ]] && CONF_FOLDER="${jarfolder}"

# shellcheck source=/dev/null

[[ -r "${CONF_FOLDER}/${configfile}" ]] && source "${CONF_FOLDER}/${configfile}"

# ANSI Colors

echoRed() { echo $'\e[0;31m'"$1"$'\e[0m'; }

echoGreen() { echo $'\e[0;32m'"$1"$'\e[0m'; }

echoYellow() { echo $'\e[0;33m'"$1"$'\e[0m'; }

# Initialize PID/LOG locations if they weren't provided by the config file

[[ -z "$PID_FOLDER" ]] && PID_FOLDER="/var/run"

[[ -z "$LOG_FOLDER" ]] && LOG_FOLDER="/var/log"

! [[ "$PID_FOLDER" == /* ]] && PID_FOLDER="$(dirname "$jarfile")"/"$PID_FOLDER"

! [[ "$LOG_FOLDER" == /* ]] && LOG_FOLDER="$(dirname "$jarfile")"/"$LOG_FOLDER"

! [[ -x "$PID_FOLDER" ]] && echoYellow "PID_FOLDER $PID_FOLDER does not exist. Falling back to /tmp" && PID_FOLDER="/tmp"

! [[ -x "$LOG_FOLDER" ]] && echoYellow "LOG_FOLDER $LOG_FOLDER does not exist. Falling back to /tmp" && LOG_FOLDER="/tmp"

# Set up defaults

[[ -z "$MODE" ]] && MODE="auto" # modes are "auto", "service" or "run"

[[ -z "$USE_START_STOP_DAEMON" ]] && USE_START_STOP_DAEMON="true"

# Create an identity for log/pid files

if [[ -z "$identity" ]]; then

if [[ -n "$init_script" ]]; then

identity="${init_script}"

else

identity=$(basename "${jarfile%.*}")_${jarfolder//\//}

fi

fi

# Initialize log file name if not provided by the config file

[[ -z "$LOG_FILENAME" ]] && LOG_FILENAME="${identity}.log"

# Initialize stop wait time if not provided by the config file

[[ -z "$STOP_WAIT_TIME" ]] && STOP_WAIT_TIME="60"

# Utility functions

checkPermissions() {

touch "$pid_file" &> /dev/null || { echoRed "Operation not permitted (cannot access pid file)"; return 4; }

touch "$log_file" &> /dev/null || { echoRed "Operation not permitted (cannot access log file)"; return 4; }

}

isRunning() {

ps -p "$1" &> /dev/null

}

await_file() {

end=$(date +%s)

let "end+=10"

while [[ ! -s "$1" ]]

do

now=$(date +%s)

if [[ $now -ge $end ]]; then

break

fi

sleep 1

done

}

# Determine the script mode

action="run"

if [[ "$MODE" == "auto" && -n "$init_script" ]] || [[ "$MODE" == "service" ]]; then

action="$1"

shift

fi

# Build the pid and log filenames

PID_FOLDER="$PID_FOLDER/${identity}"

pid_file="$PID_FOLDER/${identity}.pid"

log_file="$LOG_FOLDER/$LOG_FILENAME"

# Determine the user to run as if we are root

# shellcheck disable=SC2012

[[ $(id -u) == "0" ]] && run_user=$(ls -ld "$jarfile" | awk '{print $3}')

# Run as user specified in RUN_AS_USER

if [[ -n "$RUN_AS_USER" ]]; then

if ! [[ "$action" =~ ^(status|run)$ ]]; then

id -u "$RUN_AS_USER" || {

echoRed "Cannot run as '$RUN_AS_USER': no such user"

exit 2

}

[[ $(id -u) == 0 ]] || {

echoRed "Cannot run as '$RUN_AS_USER': current user is not root"

exit 4

}

fi

run_user="$RUN_AS_USER"

fi

# Issue a warning if the application will run as root

[[ $(id -u ${run_user}) == "0" ]] && { echoYellow "Application is running as root (UID 0). This is considered insecure."; }

# Find Java

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

echo "Unable to find Java"

exit 1

fi

arguments=(-Dsun.misc.URLClassPath.disableJarChecking=true $JAVA_OPTS -jar "$jarfile" $RUN_ARGS "$@")

# Action functions

start() {

if [[ -f "$pid_file" ]]; then

pid=$(cat "$pid_file")

isRunning "$pid" && { echoYellow "Already running [$pid]"; return 0; }

fi

do_start "$@"

}

do_start() {

working_dir=$(dirname "$jarfile")

pushd "$working_dir" > /dev/null

if [[ ! -e "$PID_FOLDER" ]]; then

mkdir -p "$PID_FOLDER" &> /dev/null

if [[ -n "$run_user" ]]; then

chown "$run_user" "$PID_FOLDER"

fi

fi

if [[ ! -e "$log_file" ]]; then

touch "$log_file" &> /dev/null

if [[ -n "$run_user" ]]; then

chown "$run_user" "$log_file"

fi

fi

if [[ -n "$run_user" ]]; then

checkPermissions || return $?

if [ $USE_START_STOP_DAEMON = true ] && type start-stop-daemon > /dev/null 2>&1; then

start-stop-daemon --start --quiet \

--chuid "$run_user" \

--name "$identity" \

--make-pidfile --pidfile "$pid_file" \

--background --no-close \

--startas "$javaexe" \

--chdir "$working_dir" \

-- "${arguments[@]}" \

>> "$log_file" 2>&1

await_file "$pid_file"

else

su -s /bin/sh -c "$javaexe $(printf "\"%s\" " "${arguments[@]}") >> \"$log_file\" 2>&1 & echo \$!" "$run_user" > "$pid_file"

fi

pid=$(cat "$pid_file")

else

checkPermissions || return $?

"$javaexe" "${arguments[@]}" >> "$log_file" 2>&1 &

pid=$!

disown $pid

echo "$pid" > "$pid_file"

fi

[[ -z $pid ]] && { echoRed "Failed to start"; return 1; }

echoGreen "Started [$pid]"

}

stop() {

working_dir=$(dirname "$jarfile")

pushd "$working_dir" > /dev/null

[[ -f $pid_file ]] || { echoYellow "Not running (pidfile not found)"; return 0; }

pid=$(cat "$pid_file")

isRunning "$pid" || { echoYellow "Not running (process ${pid}). Removing stale pid file."; rm -f "$pid_file"; return 0; }

do_stop "$pid" "$pid_file"

}

do_stop() {

kill "$1" &> /dev/null || { echoRed "Unable to kill process $1"; return 1; }

for i in $(seq 1 $STOP_WAIT_TIME); do

isRunning "$1" || { echoGreen "Stopped [$1]"; rm -f "$2"; return 0; }

[[ $i -eq STOP_WAIT_TIME/2 ]] && kill "$1" &> /dev/null

sleep 1

done

echoRed "Unable to kill process $1";

return 1;

}

force_stop() {

[[ -f $pid_file ]] || { echoYellow "Not running (pidfile not found)"; return 0; }

pid=$(cat "$pid_file")

isRunning "$pid" || { echoYellow "Not running (process ${pid}). Removing stale pid file."; rm -f "$pid_file"; return 0; }

do_force_stop "$pid" "$pid_file"

}

do_force_stop() {

kill -9 "$1" &> /dev/null || { echoRed "Unable to kill process $1"; return 1; }

for i in $(seq 1 $STOP_WAIT_TIME); do

isRunning "$1" || { echoGreen "Stopped [$1]"; rm -f "$2"; return 0; }

[[ $i -eq STOP_WAIT_TIME/2 ]] && kill -9 "$1" &> /dev/null

sleep 1

done

echoRed "Unable to kill process $1";

return 1;

}

restart() {

stop && start

}

force_reload() {

working_dir=$(dirname "$jarfile")

pushd "$working_dir" > /dev/null

[[ -f $pid_file ]] || { echoRed "Not running (pidfile not found)"; return 7; }

pid=$(cat "$pid_file")

rm -f "$pid_file"

isRunning "$pid" || { echoRed "Not running (process ${pid} not found)"; return 7; }

do_stop "$pid" "$pid_file"

do_start

}

status() {

working_dir=$(dirname "$jarfile")

pushd "$working_dir" > /dev/null

[[ -f "$pid_file" ]] || { echoRed "Not running"; return 3; }

pid=$(cat "$pid_file")

isRunning "$pid" || { echoRed "Not running (process ${pid} not found)"; return 1; }

echoGreen "Running [$pid]"

return 0

}

run() {

pushd "$(dirname "$jarfile")" > /dev/null

"$javaexe" "${arguments[@]}"

result=$?

popd > /dev/null

return "$result"

}

# Call the appropriate action function

case "$action" in

start)

start "$@"; exit $?;;

stop)

stop "$@"; exit $?;;

force-stop)

force_stop "$@"; exit $?;;

restart)

restart "$@"; exit $?;;

force-reload)

force_reload "$@"; exit $?;;

status)

status "$@"; exit $?;;

run)

run "$@"; exit $?;;

*)

echo "Usage: $0 {start|stop|force-stop|restart|force-reload|status|run}"; exit 1;

esac

exit 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值