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 从第一个版本到最新版本,大小几乎翻了一倍;随便下一个应用几百兆。但也值得思考,我们发布的应用是不是可以更紧凑呢,里面真的这个应用需要的文件吗?

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

yum配置文件及NFS共享

一 yum配置文件及命令 1 /etc/yum.conf //主配置文件 2 /etc/yum.repos.d/*.repo //yum仓库文件位置 写错一个字母就不行&#xff0c;可以ping www.google.com 测试网络 3 /var/log/yum.log //日志文件 二 yum命令 1 [rootlocalhost ~…

“盲盒+互联网”模式下的盲盒小程序带来了哪些机遇?

近几年&#xff0c;盲盒逐渐兴起&#xff0c;深受大众的喜爱。盲盒中拥有各类随机商品&#xff0c;包括玩偶手办等&#xff0c;让消费者无法自拨。盲盒拥有神秘感和不确定性&#xff0c;消费者在购买前并不知道盲盒中是什么商品&#xff0c;因此具有较大的惊喜感&#xff0c;能…

SpringBoot+beetl idea热更新解决方案

SpringBootbeetl idea热更新解决方案 第一在application中开启&#xff1a; beetl:resource-auto-check: true #热加载beetl模板&#xff0c;开发时候用第二在application中开启&#xff1a; devtools: 这个部分专门用于配置Spring Boot DevTools的相关参数。DevTools…

【LeetCode】数学精选4题

目录 1. 二进制求和&#xff08;简单&#xff09; 2. 两数相加&#xff08;中等&#xff09; 3. 两数相除&#xff08;中等&#xff09; 4. 字符串相乘&#xff08;中等&#xff09; 1. 二进制求和&#xff08;简单&#xff09; 从字符串的右端出发向左做加法&#xff0c;…

记录::关键点检测数据转化和可视化LSP、FLIC转yolov8-pose的txt

最近想试一下关键点检测的效果&#xff0c;先从yolov8-pose开始&#xff0c;不想跑coco那么大的数据集&#xff0c;就找了两个比较小的 yolov8-pose的txt数据格式如下&#xff1a; 类别、box、节点&#xff0c;数据做了归一化 可视化只显示了点&#xff0c;没有连线 参数&…

day23 修剪二叉搜索树 将有序数组转换为二叉搜索树 将二叉搜索树转换为累加树

题目1&#xff1a;669 修剪二叉搜索树 题目链接&#xff1a;669 修剪二叉搜索树 题意 将二叉搜索树的节点值修剪到[low,high]这个范围内 递归 递归三部曲&#xff1a; 1&#xff09;递归函数的参数和返回值 2&#xff09;终止条件 3&#xff09;单层递归逻辑 代码 /**…

Cobbler部署(PXE二次封装)

文章目录 Cobbler 部署一、Cobbler简介二、Cobbler的工作原理三、Cobbler安装1、操作过程命令格式2、cobbler安装图文详解 Cobbler 部署 一、Cobbler简介 Cobbler是一款Linux生态的自动化运维工具&#xff0c;基于Python2开发&#xff0c;用于自动化批量部署安装操作系统&…

MySQL运维篇(二)主从复制

一、概述 主从复制是指将主数据库的 DDL 和 DML 操作通过 二进制日志 传到从库服务器中&#xff0c;然后在从库上对这些日志重新执行&#xff08;也叫重做&#xff09;&#xff0c;从而使得从库和主库的数据保持同步。 MySQL 支持一台主库同时向多台从库进行复制&#xff0c; 从…

网络安全防护部署所需要注意的几点

顶层设计概念 考虑项目各层次和各要素&#xff0c;追根溯源&#xff0c;统揽全局&#xff0c;在最高层次上寻求问题的解决之道 顶层设计”不是自下而上的“摸着石头过河”&#xff0c;而是自上而下的“系统谋划” 网络安全分为 物理、网络、主机、应用、管理制度 边界最强 接…

springboot109新闻稿件管理系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的新闻稿件管理系统 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获…

卡尔曼滤波、马尔科夫模型、粒子滤波、TSP问题知识点回顾

前面有小结了概率论、线性代数、现代控制理论的一些知识点&#xff0c;这边再来回顾下之前看过了关于卡尔曼滤波、马尔科夫模型、粒子滤波、动态规划中的TSP问题&#xff0c;这边也只是知其形&#xff0c;便于日后应用到一些实际案例中。 一.卡尔曼滤波 这边只是记录要点&…

浪花 - 主页开发

一、简易版主页 1. 主页展示用户列表 <template><!--推荐用户列表--><van-cardv-for"user in userList":desc"user.profile":title"${user.username}(${user.planetCode})":thumb"user.avatarUrl"><template #…

利用预训练模型SKEP进行情感分析

项目地址&#xff1a;文本情感分析 - 飞桨AI Studio星河社区 (baidu.com) baidu/Senta: Baidus open-source Sentiment Analysis System. (github.com) 本项目将详细全面介绍情感分析任务的两种子任务&#xff0c;句子级情感分析和目标级情感分析。 同时演示如何使用情感分析…

【RabbitMQ】快速入门及基本使用

一、引言 1、、消息队列 Ⅰ、什么是消息队列&#xff1f; 消息队列是一种进程间通信或同一进程的不同线程间的通信方式&#xff0c;软件的贮列用来处理一系列的输入&#xff0c;通常是来自用户。消息队列提供了异步的通信协议&#xff0c;每一个贮列中的纪录包含详细说明的数据…

一文看完String的前世今生,内容有点多,请耐心看完!

写在开头 String字符串作为一种引用类型&#xff0c;在Java中的地位举足轻重&#xff0c;也是代码中出现频率最高的一种数据结构&#xff0c;因此&#xff0c;我们需要像分析Object一样&#xff0c;将String作为一个topic&#xff0c;单独拿出来总结&#xff0c;这里面涉及到字…

虹科分享 | Redis与MySQL协同升级企业缓存

文章速览&#xff1a; MySQL为什么需要Redis EnterpriseRedis Enterprise带来哪些优势Redis Enterprise与MySQL协同 传统的MySQL数据库在处理大规模应用时已经到了瓶颈&#xff0c;Redis Enterprise怎样助力突破这一瓶颈&#xff1f;Redis Enterprise与MYSQL共同用作企业级缓存…

第二次作业+第三次作业

第二次作业第三次作业 第二次作业 题目&#xff1a; 网站需求&#xff1a; ​ 1.基于域名[www.openlab.com](http://www.openlab.com)可以访问网站内容为 welcome to openlab!!! 2.给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于[ww…

[全连接神经网络]Transformer代餐,用MLP构建图像处理网络

一、MLP-Mixer 使用纯MLP处理图像信息&#xff0c;其原理类似vit&#xff0c;将图片进行分块(patch)后展平(fallten)&#xff0c;然后输入到MLP中。理论上MLP等价于1x1卷积&#xff0c;但实际上1x1卷积仅能结合通道信息而不能结合空间信息。根据结合的信息不同分为channel-mixi…

hash应用

目录 一、位图 1.1、引出位图 1.2、位图的概念 1.3、位图的应用 1.4、位图模拟实现 二、布隆过滤器 2.1、什么是布隆过滤器 2.2、布隆过滤器应用的场景 2.3、布隆过滤器的原理 2.4、布隆过滤器的查找 2.5、布隆过滤器的插入 2.6、布隆过滤器的删除 2.7、布隆过滤器…

深入解析JavaScript中箭头函数的用法

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 箭头函数(Arrow function)是JavaScript ES6中引入的一大特性。箭头函…