Java 应用部署包优化经验分享

背景

最近接手了一个 2018 年的老项目,因为太久远了,功能上的代码不敢乱动,虽然是老项目,但最近一年也在持续加功能,功能不稳定,于是我就进入了救火式改 Bug 的状态。

功能不能妄动,但是这个项目还有一个问题,打包模块打出的全量包部署不起来。拿到这个项目的部署包,400 多兆,网速慢的情况下,下载、上传都得好半天。分析了一下部署包,决定先优化一下,本文记录这个 Java 应用的部署包优化过程。

优化主要是清理 Java 依赖,内容有:

  1. 无用依赖
  2. 测试相关的依赖
  3. 相同 jar 的不同版本
  4. 有冲突的 jar
  5. 容器自带、但是项目无用的包
  6. 第三方组件中的无用文件,如 docs、.cmd 、NOTICES、src 源码等

无用依赖包

项目创建初期的 pom 文件大概是从别的旧项目拷贝过来的,没有做过清理,里面有一些引用包但是工程中没有用到的。比如 ,ftpserver-core、sshd-core,注释掉这些引用后,项目编译能通过,打包后生成的 lib 包中也没有这些模块,说明就是无用的,可以清理掉。

此外,项目初期引 jar 的时候,有必要搞明白引入的包是实现什么功能的,项目是否用得到。如果不确定能否用到,可以只在 maven 父工程的依赖管理中定义,子模块需要的时候再引入。

测试相关的依赖包

maven 项目引入模块时,虽然 scope 设置为 test,但是打包的时候,这些 jar 还是会被加入到第三方依赖 lib 目录下。所以在整理项目部署包的时候,需要手动剔除掉各种测试相关的依赖包。

主要有 junit、自动化测试框架、第三方测试工具类等,搜索出来:
在这里插入图片描述
这些都可以清理掉。

相同 jar 的不同版本

部署包中存在一些名称相同、版本号不一样的 jar ,需要手动清理。

比如 netty 的低版本和 netty-all 高版本,如果引用了 netty-all ,就可以清理掉 netty 低版本了,netty-moduleX 开头的低版本=netty-all 高版本,都引入就存在冗余了:
在这里插入图片描述
还有 JDK 的 tools 包:
在这里插入图片描述
这些都是磁盘蛀虫,项目部署包中没有,而且两个文件都是一样的只是版本不同。 JDK 中已经有了,如果真的要用,用 JDK/jre/ext 下的就可以了。

有冲突的包

Java 框架发展过程中,有一些相互冲突的包,是不应该同时引入的。同时引入,而且能正常运行,只能说是幸运。

比如,servlet-api-2.5.jar 和 javax.servlet-api-3.1.0.jar。servlet-api-2.5.jar 这个版本,可以直接清理掉。

容器引用但是项目完全无用的包

比如项目没有用到 websocket 功能,但是使用的容器自带了这些包:
在这里插入图片描述
清理掉,积少成多,能少则少!

多模块公共 jar 共享

这个项目组件比较少,一个后台、一个前端,但是两个模块有公共的 jar ,梳理出来后,公共包有几十兆。而项目源码包也两个模块共同的包,每次发布补丁的时候都要同时更新两个组件的依赖。

所以,彻底的优化方案是,对项目模块的 jar 进行分类,按当前工程分为四个 jar 包目录:

  1. commonLib:所有模块公共引用的包
  2. moduleALib:模块 A 引用的包
  3. moduleBLib:模块 B 引用的包
  4. dynamicLib:应用中支持动态上传的包

计算模块 A 和模块 B 公共依赖的方法,用 Shell 脚本就可以完成:

进入 moduleA 全量包目录,ll|grep -v 总量|awk '{print $NF}' > /home/alib.log
进入 moduleB 全量包目录,ll|grep -v 总量|awk '{print $NF}' > /home/blib.log
file1="/home/alib.log" #第一个文件名
file2="/home/blib2.log" #第二个文件名
#通过comm命令获取公共行 
common_lines=$(comm -12 <(sort "$file1") <(sort "$file2")) 
echo "$common_lines" > /home/commlib.log

计算出公共包后,就可以将模块 A、B 全量包中的公共文件移除到公共目录了

进入 moduleA 全量包目录,cat /home/commlib.log |xargs -I file mv file /home/commonlib
进入 moduleB 全量包目录,cat /home/commlib.log |xargs -I file mv file /home/commonlib

这样就得到了整个应用的最终依赖包:
在这里插入图片描述
整个应用的依赖包放在一起集中管理,目录清晰,更新方便。目录结构规划好之后了,就需要优化启动脚本了,应用通过 -cp 参数将依赖包目录下所有的 jar 文件拼接起来、然后启动的,很多 Java 工程都是用这个方式启动的,比如 Kafka、IDEA 启动某个主类。
在这里插入图片描述
这种设置 Java 类路径的方法,有一个大问题,就是如果依赖包过多时,进程的启动命令会拼接的很长,比如上面这个,一屏都看不到这个进程的全貌。

有三种方法可以改善这个问题:

  1. -cp 拼接路径可以用通配符-cp /xx/lib/*:/lib/*
  2. -Djava.ext.dirs:这是普通 Java 应用的参数。
  3. -Dloader.path:SpringBoot 引用的启动参数。

这个工程是原生的 SpringMVC 项目,尝试了第二种方法,但是找不到主类,最终选择了第一种方法。

修改应用中组件 A、B 的启动脚本,将拼接 -cp 参数的部分直接改为当前应用部署包中 lib 目录:

模块 A 的启动脚本中拼接依赖的地方 moduleALib = moduleALib+commonLib
CLASSPATH=${APP_HOME}/lib/commonLib/*:${APP_HOME}/lib/moduleALib/*
同理修改模块 B 的启动脚本。

第三方组件的无关文件

最后一点可以优化的是第三方组件中的无关文件了,部署包中显然用不上。
主要有:

  1. docs :组件说明文档。
  2. src :源码。
  3. LICENCES 文件。
  4. NOTICES 文件。
  5. cmd 启动脚本,目标是 Linux ,显然用不到 cmd 脚本。
  6. tools ,一些用来调试的工具。

启示录

经过这一些列的操作后,部署包从 400 多兆减少到了 178M,使用精简之后的部署包运行时,如果启动失败,再排查缺什么 jar ,就加上。还是比较顺利的,5轮报错后,程序就正常启动了。没有表面的错误,其他功能有没有影响,还需要继续观察。

最后一步,以精简之后的目录结构调整打包脚本,保证项目源码打出的全量包是可用的,顺手写一个补丁包打包模块。这极大方便了部署包的准备工作,按之前的流程,要拿到第一版的部署包,将项目打包出来的 6个 jar ,逐个替换部署包对应目录的文件。让工程的打包模块真正能打包,能极大减少人工操作。

部署包优化其实是个费力不讨好的事情,中途搞一半有点弄不下去了,担心优化过度后项目跑不起来了怎么办!况且,项目源码存在这么多年、经手人都多少波了,也没有人考虑过这种问题,而且豪横的项目组磁盘资源根本不是事儿,这点优化是否有必要呢?

谁让我碰到了呢!作为一个还算有点工匠精神的超级熟练程序员,真的忍不了这些问题。优化还是有成效的,至少方便自己了,经过一轮改造后,部署、发包就方便多了。

其实本文记录的工作应该是项目开发完成后,发布部署包时就应该做的工作,虽然部署包越来越大是趋势,例如:Kafka 从第一个版本到最新版本,大小几乎翻了一倍;随便下一个应用几百兆。但也值得思考,我们发布的应用是不是可以更紧凑呢,里面真的这个应用需要的文件吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/637442.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL第一次作业

一、题目要求&#xff1a; 某学校要设计一个数据库,学校的业务规则概括如下&#xff1a; 学校内班级若干,每个班级内又有学生若干。 学校开设课程若干,只有某些特定的班级能上指定的课程。 学生选修某些课程,但是在自身班级下的课程是必修。 学校定期组织考试&#xff0c;成绩…

Ubuntu20.4 Mono C# gtk 编程习练笔记(三)

Mono对gtk做了很努力的封装&#xff0c;即便如此仍然与System.Windows.Form中的控件操作方法有许多差异&#xff0c;这是gtk本身特性或称为特色决定的。下面是gtk常用控件在Mono C#中的一些用法。 Button控件 在工具箱中该控件的clicked信号双击后自动生成回调函数prototype&…

【前后端分离与不分离的区别】

Web 应用的开发主要有两种模式&#xff1a; 前后端不分离 前后端分离 理解它们的区别有助于我们进行对应产品的测试工作。 前后端不分离 在早期&#xff0c;Web 应用开发主要采用前后端不分离的方式&#xff0c;它是以后端直接渲染模板完成响应为主的一种开发模式。以前后端不…

【华为GAUSS数据库】IDEA连接GAUSS数据库方法

背景&#xff1a;数据库为华为gauss for opengauss 集中式数据库 IDEA提供了丰富的各类型数据库驱动&#xff0c;但暂未提供Gauss数据库。可以通过以下方法进行连接。 连接后&#xff0c; 可以自动检查xml文件中的sql语句是否准确&#xff0c;表名和字段名是否正确还可以直接在…

转转交易猫自带客服多模板全开源完整定制版源码

商品发布&#xff1b; 请在后台商品添加成功后&#xff0c; 再点击该商品管理&#xff0c;可重新编辑当前商品的所有信息及配图以及支付等等相关信息 可点击分享或者跳转&#xff0c;将链接地址进行发布分享 请在手机端打开访问 访问商品主要模板文件路径目录 咸鱼&#…

【实操】基于 GitHub Pages + Hexo 搭建个人博客

《开发工具系列》 【实操】基于 GitHub Pages Hexo 搭建个人博客 一、引言二、接入 Node.js2.1 下载并安装 Node.js2.2 环境变量配置 三、接入 Git3.1 下载并安装 Git3.2 环境变量配置 四、接入 Hexo4.1 安装 Hexo4.2 建站4.3 本地启动服务器 五、接入 GitHub Pages5.1 初识 G…

Java--类继承

文章目录 主要内容一.学生类1.源代码代码如下&#xff08;示例&#xff09;: 2.结果 二.交通工具类1.源代码代码如下&#xff08;示例&#xff09;: 2.结果 三.圆类1.源代码代码如下&#xff08;示例&#xff09;: 2.结果 总结 主要内容 学生类交通工具类圆类 一.学生类 具有…

ChatGLM vs ChatGPT

所有的NLP大模型 都是transformer结构 1.Mask attention 的策略不同 2.训练任务目标不同 国内大模型nb公司&#xff1a;百度、清华智谱 一、主流大模型 粉色&#xff1a;Encoder-only。 绿色&#xff1a;Encoder-Decoder&#xff0c;尽头智谱ChatGLM。 蓝色&#xff1a;…

【MongoDB】下载安装、指令操作

目录 1.下载安装 2.指令 2.1.基础操作指令 2.2.增加 2.3.查询 2.4.修改 2.5.删除 前言&#xff1a; 关于MongoDB的核心概念请移步&#xff1a; 【文档数据库】ES和MongoDB的对比-CSDN博客 1.下载安装 本文以安装Windows版本的mongodb为例&#xff0c;Linux版本的其实…

三、arcgispro二次开发创建第一个工程

忙了几天&#xff0c;总算可以创建第一工程了。 步骤一&#xff1a; 步骤二&#xff1a; 工具介绍&#xff1a; 项目创建成功&#xff1a;项目目录在解决方案资源管理器中&#xff0c;整个工具都是动态可调整的&#xff0c;如下图&#xff1a; 想把窗口放哪里就把鼠标移到红…

Linux重定向:深入理解与实践

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;晴る—ヨルシカ 0:20━━━━━━️&#x1f49f;──────── 4:30 &#x1f504; ◀️ ⏸ ▶️ ☰ &…

RHEL - 更新升级软件或系统

《OpenShift / RHEL / DevSecOps 汇总目录》 文章目录 小版本软件更新yum update 和 yum upgrade 的区别升级软件和升级系统检查软件包是否可升级指定升级软件使用的发行版本方法1方法2方法3方法4 查看软件升级类型更新升级指定的 RHSA/RHBA/RHEA更新升级指定的 CVE更新升级指定…

L1-069 胎压监测(Java)

小轿车中有一个系统随时监测四个车轮的胎压&#xff0c;如果四轮胎压不是很平衡&#xff0c;则可能对行车造成严重的影响。 让我们把四个车轮 —— 左前轮、右前轮、右后轮、左后轮 —— 顺次编号为 1、2、3、4。本题就请你编写一个监测程序&#xff0c;随时监测四轮的胎压&…

Java中的包、类、接口说明

写在开头 包、类、接口、方法、变量、参数、代码块,这些都是构成Java程序的核心部分,即便最简单的一段代码里都至少要包含里面的三四个内容,这两天花点时间梳理了一下,理解又深刻了几分。 Java中的包 Java 定义了一种名字空间,称之为包:package。一个类总是属于某个包…

Servlet系列:两种创建方式(xml,注解)

一、使用web.xml的方式配置&#xff08;Servlet2.5之前使用&#xff09; 在早期版本的Java EE中&#xff0c;可以使用XML配置文件来定义Servlet。在web.xml文件中&#xff0c;可以定义Servlet的名称、类名、初始化参数等。然后&#xff0c;在Java代码中实现Servlet接口&#x…

wayland(xdg_wm_base) + egl + opengles 最简实例

文章目录 前言一、ubuntu 下相关环境准备1. 获取 xdg_wm_base 依赖的相关文件2. 查看 ubuntu 上安装的opengles 版本3. 查看 weston 所支持的 窗口shell 接口种类二、xdg_wm_base 介绍三、egl_wayland_demo1.egl_wayland_demo2_0.c2.egl_wayland_demo3_0.c3. xdg-shell-protoco…

Chatopera 云服务支持大语言模型对话(LLM),定制您的聊天机器人

2024 年&#xff0c;Chatopera 云服务继续不断完善&#xff0c;为开发者提供最好的定制聊天机器人的工具。在过去的一年&#xff0c;用户们反映最多的建议是 Chatopera 云服务内置大语言模型的对话&#xff0c;今天 Chatopera 云服务完成了产品升级&#xff0c;满足了这个诉求。…

HCIP-BGP选路实验

一.实验拓扑图 二.详细配置 R1 interface GigabitEthernet0/0/0 ip address 12.1.1.1 255.255.255.0interface LoopBack0 ip address 1.1.1.1 255.255.255.0interface LoopBack1 ip address 10.1.1.1 255.255.255.0bgp 1 router-id 1.1.1.1 peer 12.1.1.2 as-number 2ipv4-fa…

0基础开发EtherNet/IP:协议格式,JAVA、C#、C++处理

经过一阵倒腾&#xff0c;把CIP、Ethernet/ip协议搞到手 协议的概念和理论就不提及了&#xff0c;上网随便一搜索EtherNet/IP遍地都是。 直接将协议关键点列举出来吧。 更多协议资料 www.jngbus.com 通讯软件群 30806722 这里讲解的是TCP和UDP协议的格式&#xff0c;EtherN…

如何本地部署虚拟数字克隆人 SadTalker

环境&#xff1a; Win10 SadTalker 问题描述&#xff1a; 如何本地部署虚拟数字克隆人 SadTalker 解决方案&#xff1a; SadTalker&#xff1a;学习逼真的3D运动系数&#xff0c;用于风格化的音频驱动的单图像说话人脸动画 单张人像图像&#x1f64e; ♂️音频&#x1f3…