如何在一个基座上安装更多的 Koupleless 模块?

简介: 本文由蚂蚁集团技术工程师梁栎鹏撰写,深入探讨了Koupleless模块瘦身的原理、原则及实践方法。Koupleless是云原生领域的开源项目,旨在通过模块化隔离与共享提升效率、降低成本。文章介绍了三种模块瘦身方式:复用基座类、对象及清理资源,重点讲解了自动瘦身和配置文件瘦身的具体实现,并强调了确保瘦身正确性的关键步骤。通过这些方法,可以显著减少模块体积,提高部署效率,降低资源消耗。文中还展示了实际案例的效果对比,证明了模块瘦身的有效性。欢迎读者深入了解并参与Koupleless的共建。

文|梁栎鹏(立蓬)

蚂蚁集团技术工程师

云原生领域工程师

就职于蚂蚁集团中间件团队,参与维护与建设蚂蚁 SOFAArk 和 Koupleless 开源项目,参与内部 SOFAServerless 产品的研发和实践。

本文2773字,预计阅读 7 分钟

本文属于 Koupleless 进阶系列文章第五篇,默认读者对 Koupleless 的基础概念、能力都已经了解。如果还未了解过的可以在文末​「阅读原文」​​​查看官网https://koupleless.io/ ​)

进阶系列一:Koupleless 内核系列|模块化隔离与共享带来的收益与挑战

进阶系列二:Koupleless 单进程多应用如何解决兼容问题

进阶系列三:Koupleless 内核系列 | 一台机器内 Koupleless 模块数量的极限在哪里?

进阶系列四:Koupleless 可演进架构的设计与实践|当我们谈降本时,我们谈些什么

在往期文章中,我们已经介绍了 Koupleless 的收益、挑战、应对方式及存量应用的改造成本,帮助大家了解到 Koupleless 是如何低成本地为业务研发提升效率和降低资源成本的。在实践中,开发者可以将多个 Koupleless 模块部署在同一个基座上,从而降低资源成本。那么,如何在一个基座上安装更多的模块呢?

通常来说,有三种方式:模块复用基座的类、模块复用基座的对象、模块卸载时清理资源。其中,最简单、最直接、最有效的方式是让模块复用基座的类,即模块瘦身

所谓模块瘦身,就是让模块复用基座所有依赖的类,模块打包构建时移除基座已经有的 Jar 依赖,从而让基座中可以安装更多的模块。

在最初的模块瘦身实践中,模块开发者需要感知基座有哪些依赖,并且在开发时尽量使用这些依赖,从而复用基座依赖里的类。其次,开发者需要根据基座所有依赖,判断哪些依赖可以移除并进行手工移除。在依赖移除后,还可能会出现以下场景:

  • 由于开发者误判,移除了基座没有的依赖,导致模块编译正常通过,而运行期出现❌​ClassNotFound、LinkageError 等错误​;
  • 由于模块依赖的版本和基座不同,导致模块编译正常通过,而运行期出现​❌依赖版本不兼容的错误​。

由此,引申出了 3 个关键问题:

  • 模块如何感知基座运行时的所有依赖,从而确定需要移除的依赖?
  • 如何简单地移除依赖,降低普通应用和模块相互转换的改造成本?
  • 如何保证在移除模块依赖后,模块编译时和模块运行在基座中的依赖是一样的?

下面,本文就将介绍模块瘦身原理、原则,并针对以上三个关键问题给出解决方式。

模块瘦身原理

Koupleless 底层借助 SOFAArk 框架,实现了模块与模块之间、模块和基座之间的类隔离。模块启动时会初始化各种对象,会优先使用模块的类加载器去加载构建产物 FatJar 中的 class、resource 和 Jar 包,找不到的类会委托基座的类加载器去查找。

图片

基于这套类委托的加载机制,让基座和模块共用的 class、resource 和 Jar 包通通下沉到基座中,可以让模块构建产物非常小,从而使模块消耗的 Metaspace 非常少,基座上能安装的模块数量也更多,启动也更快。

其次,模块启动后 Spring 上下文中会创建很多对象。如果启用了模块热卸载,可能无法完全回收,且安装次数过多会造成 Old 区、Metaspace 区开销大,触发频繁 FullGC。所以需要控制单模块包大小 < 5MB,这样不替换或重启基座也能热部署热卸载数百次。

在模块瘦身后,能实现以下两个好处:

  • 允许基座安装更多的模块数量,从而在合并部署场景下,​进一步降低资源成本​;在热部署热卸载场景下,不替换或重启基座就能热部署、热卸载模块更多次。
  • 提高模块安装的速度​,减少模块包大小,减少启动依赖,控制模块安装耗时 < 30秒,甚至 < 5秒。

模块瘦身原则

由上文模块瘦身原理可知,模块移除的依赖必须在基座中存在,否则模块会在运行期间出现 ClassNotFound、LinkageError 等错误。

因此,模块瘦身的原则是,​在保证模块功能的前提下,将框架、中间件等通用的依赖包尽量放置到基座中,模块中复用基座的依赖。​这样打出的模块包会更加轻量。如图:

图片

关键一:可感知的基座运行时

在基座和模块协作紧密的情况下,模块应该在开发时就感知基座正使用的所有依赖,并按需引入需要的依赖,而无需指定版本。为此,我们提供了 “基座-dependencies-starter” 的打包功能,该包在中记录了基座当前所有运行时依赖的 GAV 坐标 (GAV: GroupId、ArtifactId、Version)。打包方式非常简单,在基座的打包插件中配置必要的参数即可:

<build>
  <plugins>
    <plugin>
      <groupId>com.alipay.sofa.koupleless</groupId>
      <artifactId>koupleless-base-build-plugin</artifactId>
      <configuration>
        <!-- ... -->
        <!--生成 starter 的 artifactId(groupId和基座一致),这里需要修改!!-->
        <dependencyArtifactId>${baseAppName}-dependencies-starter</dependencyArtifactId>
        <!--生成jar的版本号-->
        <dependencyVersion>0.0.1-SNAPSHOT</dependencyVersion>
      </configuration>
    </plugin>
  </plugins>
</build>

执行 mvn 命令:

mvn com.alipay.sofa.koupleless:koupleless-base-build-plugin::packageDependency -f ${基座 bootstrap pom 对于基座根目录的相对路径}

然后,模块配置项目的 parent 为 “基座-dependencies-starter”。

<parent>
    <groupId>com.alipay</groupId>
    <artifactId>${baseAppName}-dependencies-starter</artifactId>
    <version>0.0.1</version>
</parent>

这样一来,在模块的开发过程中,开发者就能感知到基座运行时的所有依赖。

​关键二:​低成本的模块瘦身

在应用中,最简单的移除依赖的方式是把依赖的 scope 设置为 provided。但这种方式会增加普通应用转换为模块的成本,同时也意味着,如果模块要转为普通应用,需要将这些依赖配置回 compile,改造成本较高。

为了降低模块瘦身的成本,我们提供了两种配置模块瘦身的方式:基于 “基座-dependencies-starter” 自动瘦身和基于配置文件瘦身。

基于 “基座-dependencies-starter” 自动瘦身

我们提供了基于 “基座-dependencies-starter” 的自动瘦身,自动排除和基座相同的依赖​(GAV 都相同)​,保留和基座不同的依赖。配置十分简单,在模块的打包插件中配置 baseDependencyParentIdentity 标识即可:

<build>
  <plugins>
    <plugin>
      <groupId>com.alipay.sofa</groupId>
      <artifactId>sofa-ark-maven-plugin</artifactId>
      <configuration>
        <!-- ... -->
        <!-- 配置 “基座-dependencies-starter” 的标识,规范为:'${groupId}:${artifactId}' -->
        <baseDependencyParentIdentity>${groupId}:${baseAppName}-dependencies-starter</baseDependencyParentIdentity>
      </configuration>
    </plugin>
  </plugins>
</build>

基于配置文件瘦身

在配置文件中,模块开发者可以主动配置需要排除哪些依赖,保留哪些依赖。

为了进一步降低配置成本,用户仅需配置需要排除的顶层依赖,打包插件会将该顶层依赖的所有间接依赖都排除,而无需手动配置所有的间接依赖。如:

# excludes config ${groupId}:{artifactId}:{version}, split by ','
excludes=org.apache.commons:commons-lang3,commons-beanutils:commons-beanutils
# excludeGroupIds config ${groupId}, split by ','
excludeGroupIds=org.springframework
# excludeArtifactIds config ${artifactId}, split by ','
excludeArtifactIds=sofa-ark-spi

​关键三:​保证瘦身的正确性

如果基座运行时没有模块被排除的依赖,或者基座运行时中提供的依赖版本和模块预期不一致,那么模块运行过程中可能会报错。为了保证瘦身的正确性,我们需要在模块编译和发布的环节做检查。

在模块编译时,模块打包插件会检查被瘦身的依赖是否在 “基座-dependencies-starter” 中,并在控制台输出检查结果,但检查结果不影响模块的构建结果。同时,插件允许更严格的检查:配置一定参数。如果基座中不存在模块被排除的依赖,那么模块构建失败,直接报错。

在模块发布时,在发布流程中拉取基座的运行时依赖,检查是否和 “基座-dependencies-starter” 一致。如果不一致,那么卡住发布流程,开发者可根据情况去升级模块的 “基座-dependencies-starter” 或跳过该卡点。

模块瘦身效果

以某个依赖了 16 个中间件的模块为例,将模块的 parent 配置为 “基座-dependencies-starter” 自动瘦身,下表是瘦身前后的 ark-biz.jar 大小和 Metaspace 占用的对比:

图片

总结

通过上文相信大家已经了解,我们可以通过简单的配置,让模块打包更小,从而在一个基座上安装更多的 Koupleless 模块,进一步降低资源成本。

最后,再次欢迎大家使用 Koupleless 和参与共建,我们期待您宝贵的意见!

相关文章
|
5月前
|
SQL 运维 Java
蚂蚁 Flink 实时计算编译任务 Koupleless 架构改造
本文介绍了对Flink实时计算编译任务的Koupleless架构改造。为解决进程模型带来的响应慢、资源消耗大等问题,团队将进程模型改为线程模型,并借助Koupleless的类加载隔离能力实现版本和包的隔离。通过动态装配Plugin及其Classpath,以及Biz运行时仅对依赖Plugin可见的设计,大幅优化了编译任务的性能。结果表明,新架构使编译耗时降低50%,吞吐量提升5倍以上。
蚂蚁 Flink 实时计算编译任务 Koupleless 架构改造
|
分布式计算 Hadoop
hadoop 的启动和停止命令(史上最全)
sbin/start-all.sh 启动所有的Hadoop守护进程。包括NameNode、 Secondary NameNode、DataNode、ResourceManager、NodeManager sbin/stop-all.sh 停止所有的Hadoop守护进程。包括NameNode、 Secondary NameNode、DataNode、ResourceManager、NodeManager sbin/start-dfs.sh 启动Hadoop HDFS守护进程NameNode、SecondaryNameNode、DataNode sbin/stop-dfs.sh 停止Hadoop
|
3月前
|
存储 Java
银行余额生成器,银行汇款回执单生成器, 银行转账p图【仅供娱乐学习用途】
这是一套基于Java的银行交易记录模拟教学系统,包含BankSimulator和Main两个核心类。BankSimulator类通过Transaction静态嵌套类实现交易记录。
|
6月前
|
Cloud Native 安全 Java
开源之夏经验分享|Koupleless 社区黄兴抗:在开源中培养工程思维
黄兴抗是南昌师范学院电子信息工程专业的大三学生,同时也是Koupleless社区的贡献者。在开源之夏2024项目中,他参与了“存量应用自动改造成模块”的开发,旨在解决企业云原生转型中的存量应用改造难题。通过自动化工具,实现了传统应用向模块化的低成本升级,兼顾代码兼容性与独立启动功能。项目链接:[点击这里](https://summer-ospp.ac.cn/org/prodetail/2495a0376?lang=zh&list=pro)。 简介字数:238个字符。
|
9月前
|
算法 网络安全 区块链
2023/11/10学习记录-C/C++对称分组加密DES
本文介绍了对称分组加密的常见算法(如DES、3DES、AES和国密SM4)及其应用场景,包括文件和视频加密、比特币私钥加密、消息和配置项加密及SSL通信加密。文章还详细展示了如何使用异或实现一个简易的对称加密算法,并通过示例代码演示了DES算法在ECB和CBC模式下的加密和解密过程,以及如何封装DES实现CBC和ECB的PKCS7Padding分块填充。
190 4
2023/11/10学习记录-C/C++对称分组加密DES
|
IDE Java Spring
Spring Boot中的多模块项目构建
Spring Boot中的多模块项目构建
|
存储 Java
Java程序设计练习题8异常处理
Java程序设计练习题8异常处理
307 0
|
机器学习/深度学习 自然语言处理 API
什么是人机协同翻译
简要什么是人机协同翻译以及在线体验及业务场景使用感受等
什么是人机协同翻译
|
小程序 前端开发 Java
springboot+vue基本微信小程序的医院公众号建设推动医疗卫生服务现状研究
随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代,医院公众号建设推动医疗卫生服务就是信息时代变革中的产物之一。 任何系统都要遵循系统设计的基本流程,本系统也不例外,同样需要经过市场调研,需求分析,概要设计,详细设计,编码,测试这些步骤,基于Java语言、微信小程序技术设计并实现了医院公众号建设推动医疗卫生服务。系统主要包括系统首页、个人中心、用户管理、市区管理、医院公众号管理、医院信息管理、系统管理等功能模块。
329 0

热门文章

最新文章