SpringBoot在linux系统中以服务形式启动的实现原理
从spring boot的文档知道,spring boot打包一个可以在systemV直接执行的jar文件。操作也很简单,只需要在pom.xml中加入
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
通过配置后, 进行打包,生成的jar文件就可以放到 /etc/init.d/ 里面(ln -s 的软引用也可以)。这样配置之后java应用, 就可以和mysql等等的服务一样使用service命令{start|stop|status|restart|force-reload}进行操作了。或者配置成systemd服务通过systemctl命令启动。
参考:SpringBoot在linux系统中以服务形式启动一文
/etc/init.d 的简单介绍:
init.d 目录中存放的是一系列系统服务的管理(启动与停止)脚本。用service命令可执行init.d目录中相应服务的脚本。
例如执行命令“service myApp start”,可启动/etc/init.d/myApp脚本。
但是我们的myApp.jar是一个jar文件,它的真实格式是zip压缩文件,并不是一个shell脚本,那它是怎样执行的呢?
用winhex十六进制编辑器打开jar文件,在文件的开头看到了#!/bin/bash
说明它是个脚本文件。
到这里明白了, 这个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
评论区
请写下您的评论...
猜你喜欢
java基础,springboot
1736
修改成正确的用户即可
将服务设置为开机启动:
sudosystemctlenablemyApp
三、自定义JVM参数 如果是用java-jar的方式启动的java应用,我们可以直接在命令行中指定
weblog
1353
centos7版本部署的tomcat服务器,需要将8080端口放开执行如下命令:firewall-cmd--zone=public--add-port=8080/tcp--permanentfirewall
blog
linux系统运行级别
linux
1233
linux运行级别 在老的Linux发行版本中,系统运行分成不同的运行级别(runlevel),不同的级别所启动的服务搭配有所不同。较新的Linux发行版本,比如CentOS7+,已经将运行级别替
框架
7797
springboot项目启动后执行特定方法有时项目需求,需要项目启动的时候向数据库中查询一下系统属性,或者需要加载某个特定的方法。那么在springboot中可以用如下方法实现第一种实现,实现
blog
linux系统安装apache服务器
其他
4327
-yhttpd.x86_642.在线安装安装命令:yum-yinstallhttpd开启apache服务:systemctlstarthttpd.service设置apache服务开机启动:systemctlenablehttp
框架
1504
activemq下载地址:http://activemq.apache.org/activemq-5140-release.html把下载的tar.gz文件放在linux系统的/opt/文件夹下,解
spring/springmvc
6012
springmvc启动时从数据库中初始化系统常量设计的目标是,把项目的系统常量配置,放在数据库中,在项目初始化时从项目中获取配置信息,利用反射技术,把key-value对应的值自动封装进配置类。1
official
1048
chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息。谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接。
语法:
chkconfig
最新发表
归档
2018-11
12
2018-12
33
2019-01
28
2019-02
28
2019-03
32
2019-04
27
2019-05
33
2019-06
6
2019-07
12
2019-08
12
2019-09
21
2019-10
8
2019-11
15
2019-12
25
2020-01
9
2020-02
5
2020-03
16
2020-04
4
2020-06
1
2020-07
7
2020-08
13
2020-09
9
2020-10
5
2020-12
3
2021-01
1
2021-02
5
2021-03
7
2021-04
4
2021-05
4
2021-06
1
2021-07
7
2021-08
2
2021-09
8
2021-10
9
2021-11
16
2021-12
14
2022-01
7
2022-05
1
2022-08
3
2022-09
2
2022-10
2
2022-12
5
2023-01
3
2023-02
1
2023-03
4
2023-04
2
2023-06
3
2023-07
4
2023-08
1
2023-10
1
2024-02
1
2024-03
1
2024-04
1
2024-08
1
标签
算法基础
linux
前端
c++
数据结构
框架
数据库
计算机基础
储备知识
java基础
ASM
其他
深入理解java虚拟机
nginx
git
消息中间件
搜索
maven
redis
docker
dubbo
vue
导入导出
软件使用
idea插件
协议
无聊的知识
jenkins
springboot
mqtt协议
keepalived
minio
mysql
ensp
网络基础
xxl-job
rabbitmq
haproxy
srs
音视频
webrtc
javascript
加密算法
目录
没有一个冬天不可逾越,没有一个春天不会来临。最慢的步伐不是跬步,而是徘徊,最快的脚步不是冲刺,而是坚持。