项目打包时,直接用spring-boot maven plugin之类插件组装出一个包含了所有依赖的jar,即fat-jar的形式,可以直接启动,部署方便。但是并不是任何时候fat-jar都是最优解。
依赖文件分离打包在几种场景下的优化
稳定版本依赖包的分离对部署的优化
在项目开发中,依赖包我们期望是稳定的:版本的升级一定是审慎进行的,新依赖的加入也需要考量。也因此,在项目部署时,如果只打包源代码到一个thin-jar、将固定不变依赖包分离,打包得到的部署文件体积将大大减小——使用spring boot开发的项目打包成fat-jar百兆起步,但其实源代码打包的thin-jar体积很可能是KB级的。
尤其是在使用docker镜像部署jar时,对于镜像打包有优化:虽然对镜像体积减小不会有优化,但是可以将依赖文件置入单独的镜像层复用,镜像构建时多数情况下(依赖未变更的情况下)只需要重新构建源代码及它之上的层,构建速度将会得到提升。
不得不使用指定classpath的情况——依赖包面向不同OS/硬件架构
这里直接举一个例子
当我们集成luben zstd做文件压缩时,zstd的实现需要通过jni调用so/dll包,对不同OS/硬件架构需要引入不同的依赖包,此时启动项目时需要对不同的OS/硬件架构选择对应的依赖包
在很多开源项目中也可以看到,通过分离依赖包,可以根据部署情况替换依赖包版本。结合动态加载的机制,那么可以通过添加依赖jar扩展能力——比如hadoop扩展支持的shuffle实现、扩展支持的存储等
另一种情况:使用classpath引入线上依赖包
举一个例子,在大数据平台计算集群中,通常只需要客户端提供一个shade jar,而对于必须引用的hadoop依赖,通过线上环境的启动参数指定classpath引入的,这样对平台依赖的管理和升级方便。
依赖文件分离打包示例
这里给出一个简单的例子,演示如何使用maven plugin快速完成依赖分离打包和启动方式
maven默认的打包插件得到的是一个thin-jar,不过并没有自动的将依赖包归集到一起,可以使用如下配置来完成:
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
|
<dependencies>
<dependency> <groupId>com.github.luben</groupId> <artifactId>zstd-jni</artifactId> <version>1.5.4-2</version> <scope>provided</scope> </dependency>
</dependencies>
<build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.3.0</version> </plugin> </plugins> </pluginManagement> </build>
<profiles> <profile> <id>manual</id> <activation> <activeByDefault>true</activeByDefault> </activation> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <goals> <goal>copy-dependencies</goal> </goals> <phase>prepare-package</phase> <configuration> <includeScope>runtime</includeScope> <outputDirectory>${project.build.directory}/lib </outputDirectory> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <executions> <execution> <id>antrun-archive</id> <goals> <goal>run</goal> </goals> <phase>package</phase> <configuration> <target> <property name="archive.includes" value="lib/*"/> <property name="tar.destfile" value="${project.build.directory}/lib.tar"/> <zip basedir="${project.build.directory}" destfile="${project.build.directory}/lib.zip" includes="${archive.includes}"/> <tar basedir="${project.build.directory}" destfile="${tar.destfile}" includes="${archive.includes}"/> <gzip src="${tar.destfile}" destfile="${tar.destfile}.gz"/> </target> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles>
|
最终得到的target(默认构建输出路径)目录下结构为:
1 2 3 4 5 6 7
| ├─target │ ├─lib # 依赖包目录 │ │ ├─x1.y1.z1.lib.jar │ │ ├─x1.y1.z1.lib.jar │ │ ├─........... │ │ │ ├─lib-example.jar # 源代码打包得到的thin-jar
|
在linux上部署时,部署目录结构如下:
1 2 3 4 5 6 7 8 9 10 11
| ├─your_deploy_dir │ ├─lib # 依赖包目录 │ │ ├─x1.y1.z1.lib.jar │ │ ├─x1.y1.z1.lib.jar │ │ ├─........... │ │ │ ├─lib-example.jar # 源代码打包得到的thin-jar │ │ │ ├─arch-lib # OS/硬件相关的依赖包的目录 │ │ ├─zstd-jni-linux_amd64.jar │ │ ├─p.q.r-linux_amd64.jar
|
启动时通过指定classpath和mainClass启动:
1 2 3
| cd your_deploy_dir/ # com.connorma.example.LibExampleMain为主类类名 java -classpath lib-example.jar:./lib/*:./arch-lib/* com.connorma.example.LibExampleMain
|