在maven中加jar包(一个小技巧Maven打)(1)

大家好,我是互联网架构小马哥!

为了大家能够从从项目集成的苦力活中解放出来,最近这一周,整理了两篇关于jenkins构建、部署的文章,感兴趣的朋友可以看看:【基础教程】、【多模块构建

这期间,和粉丝大佬交流到关于Maven构建的优化、压缩的问题;一段简单的配置,就可以将包JAR包的大小缩小近百倍,实际的开发部署过程中,非常的实用的一个小技巧,在这里分享给各位;

SpringBoot项目的依赖,我们一般都会采用Maven管理,整个项目,一般都分为以下几部分:

而整个项目通常会被构建成一个jar,上传到服务器运行;整个Jar包中,三方依赖会被一并打包进去,占用空间最大的,也就是这部分依赖包;

比如下面这个最基本的测试 SpringBoot 项目,就一个简单的hello world接口,但是打包出来的Jar就有20多M;

在maven中加jar包(一个小技巧Maven打)(2)

把Jar包解压之后,发现三方依赖竟然比整个Jar包都大(可能压缩的原因),自己的代码只有100多K;

这还只是一个最基础的项目,如果业务复杂,依赖多,光是三方包可能就占用几十、几百M之多;

由于依赖包变化小,占用空间大的特点,大部分情况是第一次添加之后,后面很少会去调整;但每次修改哪怕是一行代码,都需要重新把他们构建Jar中去,往服务器上传、发布,白白消耗了大量的资源、带宽以及时间;

那能否将三方依赖和自己的业务代码分开打包呢?答案是:可以的

1依赖拆分配置

只需要在项目pom.xml文件中添加下面的配置:

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <finalName>${project.build.finalName}</finalName> <!--解决windows命令行窗口中文乱码--> <jvmArguments>-Dfile.encoding=UTF-8</jvmArguments> <layout>ZIP</layout> <includes> <!--这里是填写需要包含进去的jar, 必须项目中的某些模块,会经常变动,那么就应该将其坐标写进来 如果没有则non-exists ,表示不打包依赖 --> <include> <groupId>non-exists</groupId> <artifactId>non-exists</artifactId> </include> </includes> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <!--此插件用于将依赖包抽出--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> <excludeTransitive>false</excludeTransitive> <stripVersion>false</stripVersion> <includeScope>runtime</includeScope> </configuration> </execution> </executions> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> </plugins> </build>

再次构建

mvn clean package -DskipTests=true

发现target目录中多了个lib文件夹,里面保存了所有的依赖jar,自己业务相关的jar也只有小小的157kb,相比之前21M,足足小了100多倍

在maven中加jar包(一个小技巧Maven打)(3)

这种方式打的包,在项目启动时,需要通过-Dloader.path指定lib的路径:

java -Dloader.path=./lib -jar xxx.jar

在maven中加jar包(一个小技巧Maven打)(4)

虽然这样打包,三方依赖的大小并没有任何的改变,但有个很大的不同就是我们自己的业务包和依赖包分开了;在不改变依赖的情况下,也就只需要第一次上传lib目录到服务器,后续业务的调整、bug修复,在没调整依赖的情况下,就只需要上传更新小小的业务包即可;既省资源又省时间;就算是依赖变化了,也只需要更新调整的依赖,没变的依赖包咱也就不管了。

有朋友可能会说:你这业务包确实小了,但是无形中增加了对依赖包的管理,提高了管理成本

没错,这种方式,确实增加了Jar包的管理成本,多人协调开发,构建的时候,还需要专门去关注是否有人更新依赖;

不过这并不是啥大事儿,前面学习的Jenkins自动化工具,就能自动帮我们维护这个lib目录,减少人工核对,避免维护成本;


2Jenkins 管理依赖拆分的Jar

这一部分的内容依然是对前两篇关于Jenkins【基础教程】、【多模块构建】打包的完善,通过优化脚本,来实现 Jenkins 对依赖包、业务包的自动增量管理;

所以,这个并不是从0开始的教程,没看过前两篇文章的,可以先去扫一眼,然后再继续往下看;

ssh的方式

SSH的方式相比于之前的方式,只是多了管理lib中jar的过程,未调整的依赖Jar包,不上传到服务器;所以相比之前的方案,多了一个检测脚本jenkins_jar_and_lib_check.sh;他的作用就是在SSH上传之前,检测那些依赖更新了,然后只要留已更新的依赖上传到服务器;

Jenkins构建的过程jenkins_jar_and_lib_check.sh脚本

Jenkins本地校验踢出未更新的依赖和业务Jar的脚本;需要在Maven构建完,ssh传输之前使用;

完整脚本地址:https://github.com/vehang/ehang-spring-boot/blob/main/script/jenkins/jenkins_jar_and_lib_check.sh

校验步骤:

脚本部分细节说明:

SSH传输说明

前面的教程,SSH传输的都是各个项目target目录下的jar,由于这里,本地要做校验,需要缓存历史的MD5值等消息,就创建了临时文件tmp,不再使用target(每次编译都会被清空,无法缓存);

因此这里关于Jenkins的SSH传输配置就需要传输tmp目录了,包括下面脚本中使用的项目路径,也是tmp目录,不再使用target了

jenkins_restart_mini.sh

服务端检测更新,重启服务的脚本;

地址:https://github.com/vehang/ehang-spring-boot/blob/main/script/jenkins/jenkins_restart_mini.sh

校验步骤:

构建测试:

在maven中加jar包(一个小技巧Maven打)(5)

Docker的方式

Docker方式和SSH的方式有比较大的差异,采用SSH的方式,一般是明确知道那些服务器,然后直接上传;但采用Docker,最终服务在那些机器上运行,就不一定了,比如使用了K8S;

那就意味着,服务所需的包、依赖,都必须打到Docker镜像中,以方便容器启动时使用;但这似乎又违背了本文的意图,哪怕是只有业务更新,也需要把所有的依赖添加到镜像中去;

既然业务包也依赖包能拆分,业务(app)镜像和依赖(lib)镜像分开也就能解决这个问题了,如下图:

在maven中加jar包(一个小技巧Maven打)(6)

示例项目:https://github.com/vehang/ehang-spring-boot/tree/main/spring-boot-012-tools-jenkins-mini-build

在maven中加jar包(一个小技巧Maven打)(7)

目录说明:

Jenkins构建Docker镜像并启动的过程依赖镜像的Dockerfile

目录在/docker/lib/Dockerfile

FROM openjdk:8 # 同步时区 ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # 将lib目录下的所有jar全部拷贝到镜像里面 ADD ./*.jar /lib/

就是将所有的目录包拷贝到镜像中去;

构建镜像:

docker build -t lib-jenjins-mini-build:latest ./docker/lib/.

最终会构建出一个名为lib-jenjins-mini-build:latest的镜像

业务镜像的Dockerfile

目录在/docker/app/Dockerfile

# 集成自依赖基础镜像 FROM lib-jenjins-mini-build:latest # 将当前目录下的jar拷贝到容器类 ADD ./*.jar /app.jar # 监听端口 EXPOSE 18092 # 启动 ENTRYPOINT ["java","-Dloader.path=/lib","-Djava.security.egd=file:/dev/./urandom" \ ,"-XX: UnlockExperimentalVMOptions","-XX: UseCGroupMemoryLimitForHeap" \ ,"-jar", "/app.jar" ]

docker-image-build.sh

用于构建基础镜像和业务镜像的脚本;

脚本地址:https://github.com/vehang/ehang-spring-boot/blob/main/spring-boot-012-tools-jenkins-mini-build/docker/docker-image-build.sh

构建测试:

在maven中加jar包(一个小技巧Maven打)(8)

在maven中加jar包(一个小技巧Maven打)(9)

再回看一下部署过程,发现曾经最耗时的部分,现在一下变得丝滑了好多...

原文链接:https://mp.weixin.qq.com/s/qniPev0onMfmPzxigB5MaA

,