Ref
- SpringBoot – using the assembly for package tutorial 1 (startup scripts, read external configuration files) | Blog
- SpringBoot – Project Packaging tutorial 2 with Assembly (packaging project code and dependencies separately)
Two deployment modes of the Spring Boot project
Currently, Spring Boot projects are deployed in two common ways
- One is to use docker containers to deploy. Build the Spring Boot application into a Docker image, and then start the image through the container. This method is very convenient when large-scale applications need to be deployed and extended. It belongs to the current industrial deployment scheme, but docker’s ecosphere technology needs to be mastered.
- The other is to use
FatJar
Direct deployment startup (putting a JAR and all its dependent third-party jars into a package called FatJar). This is a simple way to deploy applications for many beginners or on a very small scale.
The advantage of the Assembly
The Fatjar deployment scheme described above has some drawbacks. Because if we directly build a FatJar of Spring Boot for deployment by operation and maintenance personnel, the entire configuration file is hidden in the JAR, and it becomes very difficult to modify the configuration file for different environments. If the environment is uncertain, or you need to start a script to start a project, this direct jar approach will require a lot of work later on.
Packaging Spring Boot as a service with Assembly solves the two problems mentioned above
- Enables Spring Boot to load configuration files outside the JAR.
- This script is usually shell or Bat in Windows. With the Spring Boot application service script, it is easy to start and stop Spring Boot applications.
Project configuration
1- Add plug-ins
(1) Edit the project’s POM.xml file and add the Assembly package plug-in.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<! Maven uses the Maven assembly plugin.
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<descriptors>
<! -- Specific configuration file -->
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<! -- Bind to maven operation type -->
<phase>package</phase>
<! -- Run once -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Copy the code
(2) As you can see from the code above, put the assembly configuration in the main/assembly directory (the files in that directory will be created later).
Write service start/stop scripts
Start by creating a bin folder in the Assembly directory, and then create a start.sh file in that folder. This is the startup script for Linux.
Tip: We don’t need to set the project name, jar package name at the beginning, here we use parameter variables, these parameters are automatically replaced with the value of Properties in profiles of THE POM after the project is packaged (the Assembly profile requires property replacement enabled). The same goes for the other two profiles below.
#! JAR_NAME="${project.build.finalname}. Jar 'dirname $0' # bin absolute directory BIN_DIR= 'PWD' # return to the root directory CD.. # print absolute path of project root directory # 'PWD' execute system commands and get results DEPLOY_DIR= 'PWD' # Absolute directory of external configuration files, if directories require/end, If you specify a directory, Spring will read all configuration files in the directory CONF_DIR=$DEPLOY_DIR/config # SERVER_PORT= 'sed '/server.port/! d; = / / 's /. * config/application. The properties | tr -d' \ r '` # for application port number SERVER_PORT = ` sed - nr'/port: [0-9] + / s /. * the port: +([0-9]+).*/\1/p' config/application.yml` PIDS=`ps -f | grep java | grep "$CONF_DIR" |awk '{print $2}'` if [ "$1" = "status" ]; then if [ -n "$PIDS" ]; then echo "The $SERVER_NAME is running... !" echo "PID: $PIDS" exit 0 else echo "The $SERVER_NAME is stopped" exit 0 fi fi if [ -n "$PIDS" ]; then echo "ERROR: The $SERVER_NAME already started!" echo "PID: $PIDS" exit 1 fi if [ -n "$SERVER_PORT" ]; then SERVER_PORT_COUNT=`netstat -tln | grep $SERVER_PORT | wc -l` if [ $SERVER_PORT_COUNT -gt 0 ]; then echo "ERROR: The $SERVER_NAME port $SERVER_PORT already used!" Exit 1 fi fi # Project log output absolute path LOGS_DIR=$DEPLOY_DIR/logs # If logs folder does not exist, create folder if [! -d $LOGS_DIR]; then mkdir $LOGS_DIR fi STDOUT_FILE=$LOGS_DIR/catalina.log # JVM Configuration JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true " JAVA_DEBUG_OPTS="" if [ "$1" = "debug" ]; then JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n " fi JAVA_JMX_OPTS="" if [ "$1" = "jmx" ]; then JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false " fi JAVA_MEM_OPTS="" BITS=`java -version 2>&1 | grep -i 64-bit` if [ -n "$BITS" ]; then JAVA_MEM_OPTS=" -server -Xmx512m -Xms512m -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 " else JAVA_MEM_OPTS=" -server -Xms512m -Xmx512m -XX:PermSize=128m -XX:SurvivorRatio=2 -XX:+UseParallelGC " fi # LOG_IMPL_FILE=log4j2.xml LOGGING_CONFIG="" if [-f "$CONF_DIR/$LOG_IMPL_FILE"] then LOGGING_CONFIG="-Dlogging.config=$CONF_DIR/$LOG_IMPL_FILE" fi CONFIG_FILES=" -Dlogging.path=$LOGS_DIR $LOGGING_CONFIG -Dspring.config.location=$CONF_DIR/ " echo -e "Starting the $SERVER_NAME ..." nohup java $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS $CONFIG_FILES -jar $DEPLOY_DIR/lib/$JAR_NAME > $STDOUT_FILE 2>&1 & COUNT=0 while [ $COUNT -lt 1 ]; do echo -e ".\c" sleep 1 if [ -n "$SERVER_PORT" ]; then COUNT=`netstat -an | grep $SERVER_PORT | wc -l` else COUNT=`ps -f | grep java | grep "$DEPLOY_DIR" | awk '{print $2}' | wc -l` fi if [ $COUNT -gt 0 ]; then break fi done echo "OK!" PIDS=`ps -f | grep java | grep "$DEPLOY_DIR" | awk '{print $2}'` echo "PID: $PIDS" echo "STDOUT: $STDOUT_FILE"Copy the code
Then create a stop.sh file, which is the stop script for Linux, as follows.
#! APPLICATION="${project.artifactid}" # APPLICATION="${project.build.finalName}. Jar "# Find PI by project name, Then kill 9 pid pid = $(ps - ef | grep "${APPLICATION_JAR}" | grep -v grep | awk '{print $2}') if [[z "$pid"]] then echo ${APPLICATION} is already stopped else echo kill ${PID} kill -9 ${PID} echo ${APPLICATION} stopped successfully fiCopy the code
Finally, create a start.bat file, which is the startup script for Windows.
echo off set APP_NAME=${project.build.finalName}.jar set LOG_IMPL_FILE=log4j2.xml set LOGGING_CONFIG= if exist .. /config/%LOG_IMPL_FILE% ( set LOGGING_CONFIG=-Dlogging.config=.. /config/%LOGGING_CONFIG% ) set CONFIG= -Dlogging.path=.. /logs %LOGGING_CONFIG% -Dspring.config.location=.. /config/ set DEBUG_OPTS= if ""%1"" == ""debug"" ( set DEBUG_OPTS= -Xloggc:.. /logs/gc.log -verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=.. /logs goto debug ) set JMX_OPTS= if ""%1"" == ""jmx"" ( set JMX_OPTS= -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9888 -Dcom.sun.management.jmxremote.ssl=FALSE -Dcom.sun.management.jmxremote.authenticate=FALSE goto jmx ) echo "Starting the %APP_NAME%" java -Xms512m -Xmx512m -server %DEBUG_OPTS% %JMX_OPTS% %CONFIG% -jar .. /lib/%APP_NAME% echo "java -Xms512m -Xmx512m -server %DEBUG_OPTS% %JMX_OPTS% %CONFIG% -jar .. /lib/%APP_NAME%" goto end :debug echo "debug" java -Xms512m -Xmx512m -server %DEBUG_OPTS% %CONFIG% -jar .. /lib/%APP_NAME% goto end :jmx java -Xms512m -Xmx512m -server %JMX_OPTS% %CONFIG% -jar .. /lib/%APP_NAME% goto end :end pauseCopy the code
Create a packaged configuration file
Finally, we create an assembly.xml configuration file under the Assembly folder, as follows.
<assembly>
<! The assembly ID must be present and non-empty error will be appended to the end of the name of the package. If the project name is hangge-test-0.0.1-SNAPSHOT, the package name is hangge-test-0.0.1- snapshot-bin.tar.gz ->
<id>bin</id>
<! If there are N types of packages, N types of packages will be typed -->
<formats>
<format>tar.gz</format>
<! --<format>zip</format>-->
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<! -- File Settings -->
<fileSets>
<! -- 0755-> That is, users have read/write/execute permissions, and group users and other users have read/write permissions. 0644-> That is, users have read and write permission, group users and other users have read-only permission; -->
<! SRC /main/assembly/bin to the bin directory -->
<fileSet>
<directory>src/main/assembly/bin</directory>
<outputDirectory>bin</outputDirectory>
<fileMode>0755</fileMode>
<! -- If it is a script, it must be changed to Unix. If it is a script on Windows, there will be DOS writing problems.
<lineEnding>unix</lineEnding>
<filtered>true</filtered><! -- Whether to replace attributes -->
</fileSet>
<! SRC /main/assembly/config to the packaged config directory -->
<fileSet>
<directory>src/main/assembly/config</directory>
<outputDirectory>config</outputDirectory>
<fileMode>0644</fileMode>
</fileSet>
<! SRC /main/resources/config directory -->
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>/config</outputDirectory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
<include>**/*.yml</include>
</includes>
<filtered>true</filtered><! -- Whether to replace attributes -->
</fileSet>
<! Package the project startup jar into the lib directory -->
<fileSet>
<directory>target</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<! Package project documentation into the docs directory -->
<fileSet>
<directory>.</directory>
<outputDirectory>docs</outputDirectory>
<includes>
<include>*.md</include>
</includes>
<fileMode>0644</fileMode>
</fileSet>
<fileSet>
<directory>docs</directory>
<outputDirectory>docs</outputDirectory>
<fileMode>0644</fileMode>
</fileSet>
<fileSet>
<directory>src/main/assembly/docs</directory>
<outputDirectory>docs</outputDirectory>
<fileMode>0644</fileMode>
</fileSet>
</fileSets>
</assembly>
Copy the code
Packaging test
Packaging project
- We use the MVN package command to package the project.
- When packaged, a file named
xxx.tar.gz
Zip file.
- After unpacking the package, you can see the following internal directories.
Start the service
- After the package is decompressed, the following startup files are displayed in the bin directory
- Linux and macOS: Run start.sh to start the service and stop.sh to stop the service.
- Windows: Double-click start.bat to start the service
- After the service is started, the corresponding log files are generated in the logs directory (logs directory is automatically created)
Modify the configuration
- Modify the
config
The configuration file under the folder, here isapplication.properties
.
- Here we change the service port to 9090.
server.port=9090
Copy the code
- Restart the service. If the port is changed, the external configuration file is successfully loaded.
Package projects and dependencies separately
So far, when you package a project with Assembly as described above, the project code and all of the project’s dependencies are bundled into an executable JAR package.
If the project has many dependency packages, this file can be very large. Uploading the entire JAR for each release would be a waste of bandwidth and time.
Here’s how to package your project’s external dependencies separately from your own code package so that when the project changes, you can just upload the package that overwrites the changes.
Modify the configuration
- First we edit
assembly.xml
Configuration file. On the basis of the previous new third-party dependency Settings, the implementation of the third party JAR package added to the zip package lib directory.
<assembly>
<! The assembly ID must be present and non-empty error will be appended to the end of the name of the package. If the project name is hangge-test-0.0.1-SNAPSHOT, the package name is hangge-test-0.0.1- snapshot-bin.tar.gz ->
<id>bin</id>
<! If there are N types of packages, N types of packages will be typed -->
<formats>
<format>tar.gz</format>
<! --<format>zip</format>-->
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<! -- Third-party dependency Settings -->
<dependencySets>
<dependencySet>
<! -- Do not unpack the artifact, third-party jars into the lib directory of the zip file -->
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
<unpack>false</unpack>
</dependencySet>
</dependencySets>
<! -- File Settings -->
<fileSets>
<! -- 0755-> That is, users have read/write/execute permissions, and group users and other users have read/write permissions. 0644-> That is, users have read and write permission, group users and other users have read-only permission; -->
<! SRC /main/assembly/bin to the bin directory -->
<fileSet>
<directory>src/main/assembly/bin</directory>
<outputDirectory>bin</outputDirectory>
<fileMode>0755</fileMode>
<! -- If it is a script, it must be changed to Unix. If it is a script on Windows, there will be DOS writing problems.
<lineEnding>unix</lineEnding>
<filtered>true</filtered><! -- Whether to replace attributes -->
</fileSet>
<! SRC /main/assembly/config to the packaged config directory -->
<fileSet>
<directory>src/main/assembly/config</directory>
<outputDirectory>config</outputDirectory>
<fileMode>0644</fileMode>
</fileSet>
<! SRC /main/resources/config directory -->
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>/config</outputDirectory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
<include>**/*.yml</include>
</includes>
<filtered>true</filtered><! -- Whether to replace attributes -->
</fileSet>
<! Package the project startup jar into the lib directory -->
<fileSet>
<directory>target</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<! Package project documentation into the docs directory -->
<fileSet>
<directory>.</directory>
<outputDirectory>docs</outputDirectory>
<includes>
<include>*.md</include>
</includes>
<fileMode>0644</fileMode>
</fileSet>
<fileSet>
<directory>docs</directory>
<outputDirectory>docs</outputDirectory>
<fileMode>0644</fileMode>
</fileSet>
<fileSet>
<directory>src/main/assembly/docs</directory>
<outputDirectory>docs</outputDirectory>
<fileMode>0644</fileMode>
</fileSet>
</fileSets>
</assembly>
Copy the code
- Then edit the project
pom.xml
File, which was used previouslyspring-boot-maven-plugin
This plugin will dump all of the project’s dependencies into the project JAR. Let’s replace it with PImaven-jar-plugin
And set the parameters.
<build>
<plugins>
<! --> < div style = "max-width: 100%; clear: both;
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<! XML and pom.properties files are not included in the generated jar.
<addMavenDescriptor>false</addMavenDescriptor>
<manifest>
<! Add a third-party JAR to the class build path
<addClasspath>true</addClasspath>
<! -- Final location of external dependent JAR package -->
<! -- Since we put the third-party jar in the same directory as the project jar, we use it here.
<classpathPrefix>. /</classpathPrefix>
<! -- Project startup class -->
<mainClass>com.example.hanggetest.HanggeTestApplication</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<! Maven uses the Maven assembly plugin.
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<descriptors>
<! -- Specific configuration file -->
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<! -- Bind to maven operation type -->
<phase>package</phase>
<! -- Run once -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Copy the code
Packaging test
After the above two configuration changes, we repackage the project. After decompressing the generated package, it can be found that the project JAR package and the third-party JAR package in the lib folder are separated, and the project JAR volume is also very small.
The original source: www.hangge.com reproduced please keep the original link: www.hangge.com/blog/cache/…