胖瘦打包和部署
胖瘦打包比较复杂,非必要,不建议使用
简介
在现代软件开发中,如何高效地打包和部署应用程序是一个关键问题。特别是在使用 Java 开发的项目中,选择合适的打包方式可以显著影响应用程序的体积、启动速度以及部署效率。
什么是胖包和瘦包?
- 胖包:包含所有的依赖 jar 包,包括第三方库和自身代码。这种方式生成的可执行文件较大,但独立性强,方便在不同环境下部署。
- 瘦包:仅包含自身代码,不包含第三方库。依赖项需在目标环境中预先安装或单独管理。这种方式的优势在于打包文件体积较小,部署更为快速。
瘦包的优势
- 体积小:瘦包只包含应用程序自身的代码和资源,体积大幅减小。
- 部署快:由于体积更小,瘦包可以更快速地传输和部署,特别是在网络传输或持续交付场景中。
打包指南
为帮助开发者快速创建和打包 Java 应用,本文将介绍如何将 tio-boot
打包成一个胖包或瘦包的项目,并给出具体的打包和部署指南。
创建工程
创建一个 tio-boot 工程,包含底阿妈的依赖和代码
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<lombok-version>1.18.30</lombok-version>
<tio-boot.version>1.8.7</tio-boot.version>
<jfinal-aop.version>1.2.6</jfinal-aop.version>
<fastjson2.version>2.0.52</fastjson2.version>
<hotswap-classloader.version>1.2.4</hotswap-classloader.version>
<final.name>web-hello</final.name>
<main.class>com.litongjava.full.thin.HelloApp</main.class>
<assembly>full</assembly>
</properties>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>tio-boot</artifactId>
<version>${tio-boot.version}</version>
</dependency>
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>hotswap-classloader</artifactId>
<version>${hotswap-classloader.version}</version>
</dependency>
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>jfinal-aop</artifactId>
<version>${jfinal-aop.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok-version}</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
启动类
package com.litongjava.full.thin;
import com.litongjava.jfinal.aop.annotation.AComponentScan;
import com.litongjava.tio.boot.TioApplication;
@AComponentScan
public class HelloApp {
public static void main(String[] args) {
long start = System.currentTimeMillis();
TioApplication.run(HelloApp.class, args);
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
}
controller
package com.litongjava.full.thin.controller;
import com.litongjava.annotation.RequestPath;
@RequestPath("/")
public class IndexController {
@RequestPath()
public String index() {
return "index";
}
}
上面已经完成了一个简单的工程的创建
打包
打包配置
pom.xml 的 profiles 配置如下
在 pom.xml
中定义不同的 profiles
,用于区分开发环境和生产环境。使用 maven-assembly-plugin
插件来创建打包配置,根据需求选择打包成胖包或瘦包。
- 开发环境 (development):在开发环境中,使用 spring-boot-maven-plugin 插件来打包和运行应用。该配置适用于快速迭代和调试。
- 生产环境 (assembly):在生产环境中,使用 maven-assembly-plugin 插件来打包应用程序。这种配置支持生成胖包或瘦包,根据需求生成不同的部署包。
<profiles>
<!-- 开发环境配置 -->
<profile>
<id>development</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<!-- Spring Boot Maven 插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.4</version>
<configuration>
<fork>true</fork>
<mainClass>${main.class}</mainClass>
<excludeGroupIds>org.projectlombok</excludeGroupIds>
<arguments>
<argument>--mode=dev</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<!-- 生产环境配置 -->
<profile>
<id>production</id>
<build>
<plugins>
<!-- Spring Boot Maven 插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.4</version>
<configuration>
<mainClass>${main.class}</mainClass>
<excludeGroupIds>org.projectlombok</excludeGroupIds>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<!-- assembly -->
<profile>
<id>assembly</id>
<build>
<plugins>
<!-- 组装文件并压缩zip插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<!-- not append assembly id in release file name -->
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>assembly-${assembly}.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<!-- 多模块项目资源配置在classpath下 -->
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
<include>**/*.yml</include>
<include>**/*.conf</include>
</includes>
</resource>
</resources>
</build>
</profile>
</profiles>
assembly-full.xml 配置文件
- 在项目根目录 assembly-full.xml
- assembly-full.xml 文件用于定义项目在打包过程中如何组装成最终的部署包。具体来说,它配置了打包过程中的文件和依赖的处理方式。
主要内容
- 格式 (formats):定义了最终生成的包的格式,这里为 zip 格式。
- 文件集合 (fileSets):指定了需要打包的文件及其在目标路径中的存放位置,例如将所有的 .jar 文件放在 lib 目录中。
- 依赖集合 (dependencySets):将项目的所有依赖包复制到 lib 目录下,以确保运行时所需的所有依赖均已包含在包内。
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>zipPackage</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<!--所有的jar包 -->
<fileSet>
<directory>${project.build.directory}/lib</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<!-- linux启动脚本 -->
<fileSet>
<directory>${basedir}/src/main/bin</directory>
<lineEnding>unix</lineEnding>
<outputDirectory></outputDirectory>
<!-- 脚本文件在 linux 下的权限设为 755,无需 chmod 可直接运行 -->
<fileMode>755</fileMode>
<includes>
<include>*.sh</include>
</includes>
</fileSet>
<!-- 复制service文件 -->
<fileSet>
<directory>${basedir}/src/main/bin</directory>
<lineEnding>unix</lineEnding>
<outputDirectory>service</outputDirectory>
<includes>
<include>*.service</include>
</includes>
</fileSet>
<!-- windows启动脚本 -->
<fileSet>
<directory>${basedir}/src/main/bin</directory>
<lineEnding>windows</lineEnding>
<outputDirectory></outputDirectory>
<includes>
<include>*.bat</include>
</includes>
</fileSet>
</fileSets>
<!-- 依赖的 jar 包 copy 到 lib 目录下 -->
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>
项目根目录 assembly-thin.xml
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>zipPackage</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
</assembly>
打包命令
全量包
set JAVA_HOME=D:\java\jdk1.8.0_121
mvn clean package -DskipTests -Passembly -Dassembly=full
瘦子包:
set JAVA_HOME=D:\java\jdk1.8.0_121
mvn clean package -DskipTests -Passembly -Dassembly=thin
Windows 启动
java -Xverify:none -cp config;lib\*;static ${MAIN_CLASS}
eg
java -Xverify:none -cp config;lib\*;static com.litongjava.full.thin.HelloApp
linux 启动
java -Xverify:none -cp ./config:./lib/*:./static ${MAIN_CLASS}
eg
java -Xverify:none -cp ./config:./lib/*:./static com.litongjava.full.thin.HelloApp
指定启动参数
java -Xverify:none -cp ./config:./lib/*:./static com.litongjava.full.thin.HelloApp --server.port=1003
启动
启动脚本
src/main/bin/tio.sh 是一个 Shell 脚本,用于在 Unix 系统中启动、停止和管理应用程序。它提供了标准的服务管理功能,例如启动、停止、重启和查看状态。 修改 MAIN_CLASS 为工程的实际的主类名称 环境变量设置:脚本首先确定了 JAVA_HOME 和 APP_HOME 等关键变量的路径。 启动 (start):脚本检查应用是否已运行,如果未运行,则启动应用并记录其进程 ID。 停止 (stop):通过进程 ID 停止应用并清理相关的 PID 文件。 重启 (restart):先停止再启动应用。 状态 (status):查看应用的当前运行状态。
#!/bin/sh
# chkconfig: 345 99 01
# description:tio
##########################
# get app home start
###########################
PRG="$0"
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`/"$link"
fi
done
##########################
# get app home end
###########################
##########################
# custom variables start
###########################
MAIN_CLASS=""
JAVA_HOME=""
if [ -z "$JAVA_HOME" ]; then
JAVA_HOME=${JAVA_HOME:-$(dirname $(readlink -f $(which java)))/..}
fi
APP_HOME=`dirname "$PRG"`
APP_NAME=`basename "$PRG"`
PID_FILE=$APP_HOME/$APP_NAME.pid
CP=$APP_HOME/config:$APP_HOME/lib/*:$APP_HOME/static
# Java 命令行参数,根据需要开启下面的配置,改成自己需要的,注意等号前后不能有空格
# JAVA_OPTS="-Xms256m -Xmx1024m -Dundertow.port=80 -Dundertow.host=0.0.0.0"
# JAVA_OPTS="-Dundertow.port=80 -Dundertow.host=0.0.0.0"
###########################
# custom variables end
###########################
#########################
# define funcation start
##########################
lock_dir=/var/lock/subsys
lock_file=$lock_dir/$APP_NAME
createLockFile(){
[ -w $lock_dir ] && touch $lock_file
}
start(){
# Check if Java command exists
if [ ! -x "$JAVA_HOME/bin/java" ]; then
echo "Error: Java command not found at $JAVA_HOME/bin/java"
exit 1
fi
# Check if MAIN_CLASS is not empty
if [ -z "$MAIN_CLASS" ]; then
echo "Error: MAIN_CLASS is not defined. Please provide a valid main class."
exit 1
fi
[ -e $APP_HOME/logs ] || mkdir $APP_HOME/logs -p
if [ -f $PID_FILE ]
then
echo 'already running...'
else
CMD="$JAVA_HOME/bin/java -Xverify:none ${JAVA_OPTS} -cp ${CP} ${MAIN_CLASS}"
echo $CMD
nohup $CMD >> $APP_HOME/logs/$APP_NAME.log 2>&1 &
echo $! > $PID_FILE
createLockFile
echo "[Start OK]"
fi
}
stop(){
if [ -f $PID_FILE ]
then
kill `cat $PID_FILE`
rm -f $PID_FILE
echo "[Stop OK]"
else
echo 'not running...'
fi
}
restart(){
stop
start
}
status(){
cat $PID_FILE
}
##########################
# define function end
##########################
ACTION=$1
if [ -n "$2" ]; then
MAIN_CLASS=$2
fi
case $ACTION in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
status)
status
;;
*)
echo usage "{start|stop|restart|status} [main class]"
;;
esac
开机启动
创建文件 tio.service,请根据需要改成你的工程的名称
[Unit]
Description=tio
After=network.target
[Service]
ExecStart=/opt/tio-hello/tio-hello-1.0/tio.sh start
ExecStop=/opt/tio-hello/tio-hello-1.0/tio.sh stop
ExecReload=/opt/tio-hello/tio-hello-1.0/tio.sh restart
Type=forking
PrivateTmp=true
[Install]
WantedBy=multi-user.target
自动部署和启动
为简化部署流程,可以使用 deploy
工具进行自动化部署。该工具支持多平台,并通过配置文件来定义不同环境的部署步骤。
配置文件
.build.txt
:定义打包时的环境变量和命令。.deploy.toml
:定义部署时的服务器地址、压缩包路径、解压路径等。
.build.txt
[win.env]
set JAVA_HOME=D:\java\jdk1.8.0_121
[win.build]
mvn clean package -DskipTests -Passembly -Dassembly=full
[linux.env]
export JAVA_HOME=/usr/java/jdk1.8.0_121
[linux.build]
mvn clean package -DskipTests -Passembly -Dassembly=full
[mac.env]
set JAVA_HOME=~/java/jdk1.8.0_121
[mac.build]
mvn clean package -DskipTests -Passembly -Dassembly=full
.deploy.toml
[dev.upload-run]
url = "http://192.168.1.2:10405/deploy/file/upload-run/"
p = "123456"
b = ".build.txt"
file = "target/tio-boot-full-thin-demo01-1.0.0.zip"
d = "unzip/tio-boot-full-thin-demo01"
c1 = "mkdir -p /data/apps/tio-boot-full-thin-demo01 && mkdir -p backup/tio-boot-full-thin-demo01"
c2 = "[ -d /data/apps/tio-boot-full-thin-demo01 ] && cp -r /data/apps/tio-boot-full-thin-demo01 backup/tio-boot-full-thin-demo01/$(date +%Y%m%d_%H%M%S)"
c3 = "cp -r unzip/tio-boot-full-thin-demo01/* /data/apps/tio-boot-full-thin-demo01/"
c4 = "if [ $(docker ps -qa -f name=tio-boot-full-thin-demo01) ]; then docker stop tio-boot-full-thin-demo01 && docker rm -f tio-boot-full-thin-demo01; fi"
c5 = "cd /data/apps/tio-boot-full-thin-demo01/tio-boot-full-thin-demo01-1.0.0 && docker run -dit --name tio-boot-full-thin-demo01 --restart=always --net=host -v $(pwd):/app -w /app -e TZ=Asia/Shanghai -e LANG=C.UTF-8 litongjava/jdk:8u391-stable-slim java -Xverify:none -cp ./config:./lib/*:./static com.litongjava.full.thin.HelloApp"
c = "docker ps |grep tio-boot-full-thin-demo01"
胖包部署,执行命令
deploy
瘦包部署 修改.build.txt 将-Dassembly=full 修改为-Dassembly=thin