Springboot项目实现java -jar 方式部署及优化项目配置

原创

1.前言

最近公司开发新项目,原来使用的docker部署,且由外部团队负责。这次想自己内部负责运维和部署,于是选择了使用java jar包的方式直接部署,由于没有相关经验,所以自己从网上查了一些资料(见文章末尾)。

理想方案

  • 将主要配置文件放到jar外面,实现修改配置不用重新打包。
  • 第三方依赖包放到jar外的lib库中,打包不需要打入依赖,减少大小,多个项目可重用,提高部署效率。
  • 通过shell脚本实现部署过程,方便设置java虚拟机参数,方便部署操作,封装start,stop等常用操作
  • 实现java进程后台运行,关闭窗口进程正常运行。

2.实现过程

2.1 配置文件放到jar包外

2.1.1 springboot核心配置文件

Springboott默认读取核心配置文件(application.properties/application.yml)的优先级为

  • Jar包同级目录的config目录
  • Jar包同级目录
  • classPath(即resources目录)的config目录
  • classpath目录

还有一种最高优先级的方式是项目启动时通过命令的方式指定项目加载核心配置文件,命令如下

java –jar -Dspring.config.location=xxx/xxx/xxxx.properties xxxx.jar

如果Spring Boot在优先级更高的位置找到了配置,那么它会无视优先级更低的配置

2.2.2 其他资源配置文件

其他一些的配置文件,如数据源配置文件,公共资源定义配置文件(常量,FTP信息等),quartz定时器,日志等配置文件也放到jar外。

  1. Springboot项目可以通过注解方式来获取相关配置文件,所以我们也是通过注解方式让项目能够引用到jar包外部的配置文件的,如下图:
    配置文件引用
    @PropertySource里面的value有两个值,第一个是classpath下config目录下的数据源配置文件,第二个则是根据spring.profiles.path动态获取的目录,spring.profiles.path是我们在核心文件自定义的一个配置项,它的值是我们配置文件统一管理的文件夹路径,后面的ignoreResourceNotFound=true则是设定假如根据前面一个路径没有找到相关配置文件,则根据第二个路径去找。

  2. 可以在springboot配置文件中指定加载logback日志配置文件:

    logging:
      config: config/logback.xml
    

小结

  1. 可在springboot核心文件里可定义一个spring.profiles.path配置项,它的值指向我们所有配置文件统一放置的目录,核心文件自身也放置在这个目录下
  2. 代码或者配置文件里加载配置文件的地方也应该获取spring.profiles.path配置项来动态加载该路径下的配置文件
  3. pom.xml文件修改打包相关模块,将配置文件排除,这样我们打出的jar包是不含配置文件的
  4. 启动jar包时,通过命令指定加载的核心文件为spring.profiles.path下的核心文件,(Jar包同级目录的config目录执行时可不指定

2.2打包配置

pom文件中修改打包设置

<build>
		<resources>
			<resource>
				<directory>src/main/java</directory>
				<includes>
					<include>**/*.properties</include>
					<include>**/*.xml</include>
				</includes>
				<filtering>true</filtering>
			</resource>
			<resource>
				<directory>src/main/resources</directory>
				<!--打包时排除配置文件-->
                <excludes>
					<exclude>**/*.properties</exclude>
					<exclude>**/*.xml</exclude>
					<exclude>**/*.yml</exclude>
				</excludes>
				<filtering>false</filtering>
			</resource>
		</resources>
		<plugins>
			<plugin>
				<artifactId>maven-jar-plugin</artifactId>
				<configuration>
					<archive>
						<manifest>
							<addClasspath>true</addClasspath>
							<classpathPrefix>lib/</classpathPrefix>
							<useUniqueVersions>false</useUniqueVersions>
							<!--运行主类-->
							<mainClass>com.xrq.demo.Application</mainClass>
						</manifest>
						<manifestEntries>
							<Class-Path>./</Class-Path>
						</manifestEntries>
					</archive>
					<excludes>
						<exclude>*.properties</exclude>
						<exclude>*.yml</exclude>
						<exclude>*.xml</exclude>
						<exclude>config/**</exclude>
					</excludes>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<executions>
					<execution>
						<id>copy</id>
						<phase>package</phase>
						<goals>
							<goal>copy-dependencies</goal>
						</goals>
						<configuration>
							<outputDirectory>
								${project.build.directory}/lib
							</outputDirectory>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

这样打包时jar会排除配置文件,单独在目录下生成lib文件夹放第三方依赖库
改好pom.xml的build模块后,就可以通过mvn package 或者mvn install打出我们的jar包了

2.3 java启动命令

启动命令示例

nohup java -Xms500m -Xmx500m -Xmn250m -Xss256k -server -XX:+HeapDumpOnOutOfMemoryError -jar $JAR_PATH/test-0.0.1-SNAPSHOT.jar --spring.profiles.active=daily -verbose:class &

说明:

  • --spring.profiles.active=daily, 这个可以在spring-boot启动中指定系统变量,多环境(测试、预发、线上配置)的区分
    在排查jar包冲突时,可以指定启动的-verbose:class 打印出启动的应用实际加载类的路径,来排查来源。具体还有哪些可用的参数可根据自己的需求自行百度
  • jvm堆设值: -Xms500m -Xmx500m -Xmn250m -Xss256k
  • nohup 不挂断地运行命令;& 在后台运行 ,一般两个一起用。 举例:nohup command &
  • -server:服务器模式,在多个CPU时性能佳,启动慢但性能好,能合理管理内存。
  • -XX:+HeapDumpOnOutOfMemoryError:在堆溢出时保存快照

shell命令重定向绑定:

nohup command >/dev/null 2>&1 &

/dev/null 2>&1。这条命令其实分为两命令,一个是>/dev/null,另一个是2>&1。

1. /dev/null

这条命令的作用是将标准输出1重定向到/dev/null中。/dev/null代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,俗称“黑洞”。那么执行了>/dev/null之后,标准输出就会不再存在,没有任何地方能够找到输出的内容。

2. 2>&1

这条命令用到了重定向绑定,采用&可以将两个输出绑定在一起。这条命令的作用是错误输出将和标准输出同用一个文件描述符,说人话就是错误输出将会和标准输出输出到同一个地方。

linux在执行shell命令之前,就会确定好所有的输入输出位置,并且从左到右依次执行重定向的命令,所以>/dev/null 2>&1的作用就是让标准输出重定向到/dev/null中(丢弃标准输出),然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了/dev/null中,错误输出同样也被丢弃了。执行了这条命令之后,该条shell命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中。

>/dev/null 2>&1 VS 2>&1 >/dev/null

乍眼看这两条命令貌似是等同的,但其实大为不同。刚才提到了,linux在执行shell命令之前,就会确定好所有的输入输出位置,并且从左到右依次执行重定向的命令。那么我们同样从左到右地来分析2>&1 >/dev/null:

2>&1,将错误输出绑定到标准输出上。由于此时的标准输出是默认值,也就是输出到屏幕,所以错误输出会输出到屏幕。
>/dev/null,将标准输出1重定向到/dev/null中。
我们用一个表格来更好地说明这两条命令的区别:

命令 标准输出 错误输出
>/dev/null 2>&1 丢弃 丢弃
2>&1 >/dev/null 丢弃 屏幕

nohup结合

1.nohup

用途:不挂断地运行命令。

语法:nohup Command [ Arg … ] [ & ]

无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。 如果当前目录的 nohup.out
文件不可写,输出重定向到 $HOME/nohup.out 文件中。 如果没有文件能创建或打开以用于追加,那么 Command
参数指定的命令不可调用。

退出状态:
该命令返回下列出口值:

126 可以查找但不能调用 Command 参数指定的命令。
127 nohup 命令发生错误或不能查找由 Command参数指定的命令。否则,nohup 命令的退出状态是 Command 参数指定命令的退出状态。

2.&
用途:在后台运行,一般两个一起用

2.3项目管理shell脚本编写

#!/bin/bash 
APPNAME=$2
ARTIFACT=$3
VERSION=$4

usage() {
    
  echo "Usage: sh 执行脚本.sh [start|stop|restart|status] jar名称 开发环境(DEV,UAT, PRO)" 
  exit 1 
} 

#检查程序是否在运行 
is_exist(){
   
  pid=`ps -ef|grep $APPNAME/$ARTIFACT-$VERSION|grep -v grep|awk '{print $2}'`
  #-z 判断pid是否为空
  if [ -z "${pid}" ]; then
    return 1
  else
    return 0
  fi
}
#启动方法
start(){
    
  is_exist
  #$?上一次执行结果
  if [ $? -eq "0" ]; then 
    echo "${ARTIFACT} is already running. pid=${pid} ." 
  else 
    nohup java -jar -Duser.timezone=GMT+8  $APPNAME/$ARTIFACT-$VERSION.jar --logging.config=config/logback.xml >> /dev/null 2>&1 &
fi
} 
#停止方法 
stop(){
    
  is_exist 
  if [ $? -eq "0" ]; then 
    kill -9 $pid
    echo "$ARTIFACT has been stoped" 
  else 
    echo "$ARTIFACT is not running" 
  fi 
} 
#输出运行状态 
status(){
    
  is_exist 
  if [ $? -eq "0" ]; then 
    echo "${ARTIFACT} is running. Pid is ${pid}" 
  else 
    echo "${ARTIFACT} is NOT running." 
  fi 
} 
#重启 
restart(){
    
  stop 
  start 
} 
#根据输入参数,选择执行对应方法,不输入则执行使用说明 
case "$1" in 
  "start") 
    start 
  ;; 
  "stop") 
    stop 
  ;; 
  "status") 
    status 
  ;; 
  "restart") 
    restart 
  ;; 
 *) 
usage 
;; 
esac

2.4最终效果

我在服务器项目目录下增加了如下目录结构
在这里插入图片描述

  • config 用来放配置文件的目录,如yml或者其他配置文件
  • excampleAll.sh 执行语句示例
  • logs 服务运行产生的log
  • micro-serv-training-web 项目jar包文件夹
  • webserver.sh 运行脚本
    执行时使用如下脚本
 ## 启动
sh webserver.sh start micro-serv-training-web micro-serv-training-web 0.0.1-SNAPSHOT
 ##停止
sh webserver.sh stop micro-serv-training-web micro-serv-training-web 0.0.1-SNAPSHOT

参考链接:

https://blog.csdn.net/baidu_35140444/article/details/82980139
https://blog.csdn.net/xrq0508/article/details/8
https://www.cnblogs.chtom/liaojie970/p/7852977.html0050119
https://blog.csdn.net/nimoyaoww/article/details/79108101

正文到此结束