hunkier

学习笔记,开源代码,技术分享

部署Spring Boot应用

部署Spring Boot应用

SpringBoot 应用可直接打包成 Jar、War,运行时执行 java -jar *.jar/*.war 。但将其“解压”并以不同的方式运行也往往是一种优势,这在启动时(取决于jar的大小)实际上比从一个未“解压”的归档文件中运行要稍快一些。

创建应用目录:

1
2
3
# 创建应用解药目录
mkdir -p app_dir
unzip app_name.jar -d app_dir

运行应用

1
2
3
4
# 进入应用目录
cd app_dir
# 运行应用
java -Xms1g -Xmx1g -Dfile.encoding=UTF-8 org.springframework.boot.loader.WarLauncher --spring.profiles.active=hunkier --server.port=8083

此时SpringBoot应用在命令行前台运行,当键盘按下 control + C 结束时,应用程序也停止运行。
需要应用一直在后台一直运行,不受命令行退出影响。
方便应用启动和停止等操作,可将命令写成 shell 脚本 api_service.sh,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/bin/bash
#cat /etc/init.d/api_service
### BEGIN INIT INFO
# Provides: hunkier
# Required-Start: $network $local_fs $remote_fs
# Required-Stop: $network $local_fs $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: The SpringBoot control shell
# Description: Start or stop the SpringBoot application
### END INIT INFO

#应用名称
NAME="API_SERVICE"

# springboot 激活的配置文件
PROFILE="hunkier"
# web 应用服务端口
PORT=8083
# 应用部署目录
APP_DIR="/data1/webserver/api_service_${PORT}"
#日志配置文件
LOGGING_CONFIG="${APP_DIR}/config/logback-spring.xml"
# 日志目录
LOG_DIR="${APP_DIR}/logs"
# 日志文件
LOG_FILE="${LOG_DIR}/${PORT}.log"
# JVM 参数
VM_OPTIONS="-Xms1g -Xmx1g -Dfile.encoding=UTF-8"
# springboot 参数
SPRING_OPTIONS="--spring.profiles.active=${PROFILE} --server.port=${PORT} --logging.config=${LOGGING_CONFIG} --logging.pattern.path=${LOG_DIR}"
JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_311.jdk/Contents/Home"
JAVA_BIN="${JAVA_HOME}/bin/java"

# 引入系统环境
source /etc/profile

# 进入应用目录
#cd $APP_DIR
# 创建日志目录
if [ ! -d $LOG_DIR ]; then
mkdir -p $LOG_DIR
fi

# 创建日志文件
if [ ! -f $LOG_DIR ]; then
touch $LOG_FILE
fi


Info_font_prefix="\033[32m" && Error_font_prefix="\033[31m" && Info_background_prefix="\033[42;37m" && Error_background_prefix="\033[41;37m" && Font_suffix="\033[0m"

RETVAL=0
check_running(){
PID=$(ps -ef |grep ${APP_DIR} |grep $PORT |grep java |grep -v "tail" |grep -v "grep" |grep -v "init.d" |grep -v "service" |awk '{print $2}')
if [[ ! -z ${PID} ]]; then
return 0
else
return 1
fi
}
do_start(){
check_running
if [[ $? -eq 0 ]]; then
echo -e "${Info_font_prefix}[信息]${Font_suffix} ${NAME}_${PORT} (PID ${PID}) 正在运行..." && exit 0
else
ulimit -n 51200
# cd ${APP_DIR}
do_info
nohup $JAVA_BIN -cp ${APP_DIR} ${VM_OPTIONS} org.springframework.boot.loader.WarLauncher ${SPRING_OPTIONS} >> $LOG_DIR/${PORT}_nohup.log 2>&1 &
check_running
if [[ $? -eq 0 ]]; then
echo -e "${Info_font_prefix}[信息]${Font_suffix} ${NAME}_${PORT} 启动成功 !"
else
echo -e "${Error_font_prefix}[错误]${Font_suffix} ${NAME}_${PORT} 启动失败 !"
fi
fi
}
do_stop(){
check_running
if [[ $? -eq 0 ]]; then
kill -9 ${PID}
RETVAL=$?
if [[ $RETVAL -eq 0 ]]; then
echo -e "${Info_font_prefix}[信息]${Font_suffix} ${NAME}_${PORT} 停止成功 !"
else
echo -e "${Error_font_prefix}[错误]${Font_suffix} ${NAME}_${PORT} 停止失败 !"
fi
else
echo -e "${Info_font_prefix}[信息]${Font_suffix} ${NAME}_${PORT} 未运行 !"
RETVAL=1
fi
}
do_status(){
check_running
if [[ $? -eq 0 ]]; then
echo -e "${Info_font_prefix}[信息]${Font_suffix} ${NAME}_${PORT} (PID ${PID}) 正在运行..."
else
echo -e "${Info_font_prefix}[信息]${Font_suffix} ${NAME}_${PORT} 未运行 !"
RETVAL=1
fi
}
do_tail(){
echo "tail -1000f ${LOG_FILE}"
tail -1000f ${LOG_FILE}
}
do_log(){
echo "tail -f ${LOG_FILE}"
tail -f ${LOG_FILE}
}
do_restart(){
do_stop
do_start
}
do_console(){
do_start
do_log
}
do_info(){
echo "APP_NAME: ${NAME}"
echo "APP_DIR: ${APP_DIR}"
echo "APP_PORT: ${PORT}"
echo "spring 激活配置:${PROFILE}"
echo "日志目录: ${LOG_DIR}"
echo "JAVA_HOME: ${JAVA_HOME}"
echo "VM_OPTIONS: ${VM_OPTIONS}"
echo "SPRING_OPTIONS: ${SPRING_OPTIONS}"
$JAVA_BIN -version
}
case "$1" in
start|stop|restart|status|log|tail|console|info)
do_$1
;;
*)
echo "使用方法: $0 { start(启动) | stop(停止) | restart(重启) | status(状态) | log(日志) | tail(日志) | console(启动并查看日志) | info(配置信息)}"
RETVAL=1
;;
esac
exit $RETVAL



启动、停止命令

1
./api_service.sh start/stop/status

在 Unix/Linx 服务 作为 init.d 服务安装

1
sudo ln -s api_service.sh /etc/init.d/api_serice

一旦安装完毕,可以用通常的方式启动和停止该服务

1
service api_service start/stop/staus

为了更好的管理日志文件需要定制 logback 相关配置,以下为 logback-spring.xml 文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback</contextName>

<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 -->
<!-- <property name="log.path" value="./logs" />-->
<!-- 表示从 springboot 配置文件读取配置的日志文件路径 -->
<springProperty scope="context" name="logging.pattern.path" source="logging.pattern.path"/>
<springProperty scope="context" name="server.port" source="server.port"/>


<!--0. 日志格式和颜色渲染 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

<!--1. 输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>

<!--2. 输出到文档-->
<!-- 2.1 level为 INFO 日志,时间滚动输出 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pattern.path}/${server.port}.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档 -->
<fileNamePattern>${logging.pattern.path}/${server.port}-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>365</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>


<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、
以及指定<appender>。<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。
如果未设置此属性,那么当前logger将会继承上级的级别。
addtivity:是否向上级logger传递打印信息。默认是true。
<logger name="org.springframework.web" level="info"/>
<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
-->

<!--
使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常info级别:
【logging.level.org.mybatis=debug logging.level.dao=debug】
-->

<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
不能设置为INHERITED或者同义词NULL。默认是DEBUG
可以包含零个或多个元素,标识这个appender将会添加到这个logger。
-->

<!-- 4. 最终的策略 -->
<!-- 4.1 开发环境:打印控制台-->
<!-- <springProfile name="dev">-->
<!-- <logger name="com" level="debug"/>-->

<!-- </springProfile>-->


<root level="info">
<appender-ref ref="FILE" />
<!--
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
-->
</root>

<!-- 4.2 生产环境:输出到文档
<springProfile name="pro">
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</springProfile> -->

</configuration>

参考资料:
springBoot deployment:
https://docs.spring.io/spring-boot/docs/current/reference/html/deployment.html#deployment.efficient.unpacking

Accessing Command Line Properties:
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

Profiles:
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles

Logback Extensions:
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging.logback-extensions

谢谢你请我喝牛奶

欢迎关注我的其它发布渠道