如何在一个基座上安装更多的 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 和参与共建,我们期待您宝贵的意见!

相关文章
|
运维 测试技术 Serverless
Koupleless 2024 年度报告 & 2025 规划展望
2024年,Koupleless 社区在研发框架、模块化兼容性治理及运维调度等方面取得显著进展,共合并608个PR,发布10次主版本,新增20位贡献者。社区企业接入数达45家,涵盖快手、涂鸦、民生银行等。展望2025年,将继续完善模块化工具链,并建设DevOps平台和Serverless能力,助力更多企业实现降本增效。 本文4389字,预计阅读8分钟。
|
前端开发 Java 数据库连接
Spring Boot 3 整合 Mybatis-Plus 动态数据源实现多数据源切换
Spring Boot 3 整合 Mybatis-Plus 动态数据源实现多数据源切换
|
分布式计算 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
|
11月前
|
Java 数据库连接 API
开源之夏经验分享|Koupleless 社区魏照华:开源精神是场永不停歇的接力
魏照华是 University of Wollogong 计算机科学专业研究生,开源之夏 2024 Koupleless 社区贡献者。他参与开发了 Koupleless 模块打包插件的 Gradle 版本,旨在为 Gradle 用户提供与 SOFAArk 兼容的模块化研发框架支持。
交叉证书
## 证书4要素 pubkey(公钥) ,有一个其一一对应的私钥,由证书拥有者保存。 subject(名字),可以理解为证书的名字,你可以理解为 网站的域名。 issuer(颁发者的名字),即上级证书的subject。 signature(签名) ,上级证书使用私钥对当前证书进行签名的值。 ## 证书签名 假设 有 3 级 证书 ![image.png](
5463 0
|
8月前
|
存储 Java
银行余额生成器,银行汇款回执单生成器, 银行转账p图【仅供娱乐学习用途】
这是一套基于Java的银行交易记录模拟教学系统,包含BankSimulator和Main两个核心类。BankSimulator类通过Transaction静态嵌套类实现交易记录。
|
11月前
|
Cloud Native 安全 Java
开源之夏经验分享|Koupleless 社区黄兴抗:在开源中培养工程思维
黄兴抗是南昌师范学院电子信息工程专业的大三学生,同时也是Koupleless社区的贡献者。在开源之夏2024项目中,他参与了“存量应用自动改造成模块”的开发,旨在解决企业云原生转型中的存量应用改造难题。通过自动化工具,实现了传统应用向模块化的低成本升级,兼顾代码兼容性与独立启动功能。项目链接:[点击这里](https://summer-ospp.ac.cn/org/prodetail/2495a0376?lang=zh&list=pro)。 简介字数:238个字符。
|
开发框架 JavaScript 算法
了解vue3的基本特性和底层原理
Vue3的底层原理涵盖了响应式系统的Proxy-based实现、组件的模板编译与渲染更新机制、组合式API带来的逻辑组织变革,以及其他关键特性的具体实现。这些原理共同构成了Vue3强大、高效、灵活的现代前端开发框架基础。
961 2
|
SQL 监控 druid
SpringBoot配置Druid
SpringBoot配置Druid