Skip to content

Commit b26c47a

Browse files
Create index.md (#1309)
* Create index.md * Update index.md * Update index.md * Update index.md * Update index.md * Update index.md * Update index.md * Update index.md --------- Co-authored-by: Marco <[email protected]>
1 parent a142805 commit b26c47a

File tree

1 file changed

+172
-0
lines changed
  • content/zh/blog/how-can-I-install-more-koupleless-modules-on-a-single-base

1 file changed

+172
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
---
2+
title: "如何在一个基座上安装更多的 Koupleless 模块?"
3+
authorlink: "https://github.com/sofastack"
4+
description: "本文属于 Koupleless 进阶系列文章第五篇,默认读者对 Koupleless 的基础概念、能力都已经了解。如果还未了解过的可以查看官网"
5+
categories: "SOFAStack"
6+
tags: ["SOFAStack"]
7+
date: 2024-11-19T15:00:00+08:00
8+
cover: "https://img.alicdn.com/imgextra/i1/O1CN01dTwGEx1m9I7KOmUlD_!!6000000004911-0-tps-1216-521.jpg"
9+
---
10+
11+
# 如何在一个基座上安装更多的 Koupleless 模块?
12+
13+
**文 梁栎鹏(立蓬)**
14+
蚂蚁集团技术工程师
15+
16+
云原生领域工程师
17+
18+
就职于蚂蚁集团中间件团队,参与维护与建设蚂蚁 SOFAArk 和 Koupleless 开源项目,参与内部 SOFAServerless 产品的研发和实践。
19+
20+
**本文2773字,预计阅读 7 分钟**
21+
22+
本文属于 Koupleless 进阶系列文章第五篇,默认读者对 Koupleless 的基础概念、能力都已经了解。如果还未了解过的可以在文末**​「阅读原文」​**​​**查看官网**[*https://koupleless.io/​*](https://koupleless.io/)
23+
24+
进阶系列一:[Koupleless 内核系列|模块化隔离与共享带来的收益与挑战](http://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247552272&idx=1&sn=fa4b3336127fb8e391d43ff25f49a5f4&chksm=faa3e2cacdd46bdcb801a8fc948057b581574cff39c194e2f977842131d89ca273ba6c79fb5b&scene=21#wechat_redirect)
25+
26+
进阶系列二:[Koupleless 单进程多应用如何解决兼容问题](http://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247552463&idx=1&sn=945889ddc42baf19cb7c2311a613fc38&chksm=faa3e215cdd46b0308579a7558198fbd4e790bdf618a1b058c8b283649772671f7662def339c&scene=21#wechat_redirect)
27+
28+
进阶系列三:[Koupleless 内核系列 一台机器内 Koupleless 模块数量的极限在哪里?](http://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247552624&idx=1&sn=68663f07f8b79efe64ac9942f137e316&chksm=faa3e1aacdd468bc28edc9a449c452f516e7676e18968e39f26bbc41565d0251adfff2159dbe&scene=21#wechat_redirect)
29+
进阶系列四:[Koupleless 可演进架构的设计与实践|当我们谈降本时,我们谈些什么](http://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247553424&idx=1&sn=94db1883e3e0eed3f2cb0b19728030c0&chksm=faa3fe4acdd4775c2501457f064fe0473188e5a2fd24131ddc1c1cf4544d6605ec8d35e9ef05&scene=21#wechat_redirect)
30+
31+
在往期文章中,我们已经介绍了 Koupleless 的收益、挑战、应对方式及存量应用的改造成本,帮助大家了解到 Koupleless 是如何低成本地为业务研发提升效率和降低资源成本的。在实践中,开发者可以将多个 Koupleless 模块部署在同一个基座上,从而降低资源成本。***那么,如何在一个基座上安装更多的模块呢?***
32+
33+
通常来说,有三种方式:模块复用基座的类、模块复用基座的对象、模块卸载时清理资源。其中,最简单、最直接、最有效的方式是让模块复用基座的类,即**模块瘦身**
34+
35+
所谓模块瘦身,就是让模块复用基座所有依赖的类,模块打包构建时移除基座已经有的 Jar 依赖,从而让基座中可以安装更多的模块。
36+
37+
在最初的模块瘦身实践中,模块开发者需要感知基座有哪些依赖,并且在开发时尽量使用这些依赖,从而复用基座依赖里的类。其次,开发者需要根据基座所有依赖,判断哪些依赖可以移除并进行手工移除。在依赖移除后,还可能会出现以下场景:
38+
39+
* 由于开发者误判,移除了基座没有的依赖,导致模块编译正常通过,而运行期出现❌​**ClassNotFound、LinkageError 等错误**​;
40+
* 由于模块依赖的版本和基座不同,导致模块编译正常通过,而运行期出现​**❌依赖版本不兼容的错误**​。
41+
42+
由此,引申出了 3 个关键问题:
43+
44+
* 模块如何感知基座运行时的所有依赖,从而确定需要移除的依赖?
45+
* 如何简单地移除依赖,降低普通应用和模块相互转换的改造成本?
46+
* 如何保证在移除模块依赖后,模块编译时和模块运行在基座中的依赖是一样的?
47+
48+
下面,本文就将介绍模块瘦身原理、原则,并针对以上三个关键问题给出解决方式。
49+
50+
## 模块瘦身原理
51+
52+
Koupleless 底层借助 SOFAArk 框架,实现了模块与模块之间、模块和基座之间的类隔离。模块启动时会初始化各种对象,会优先使用模块的类加载器去加载构建产物 FatJar 中的 class、resource 和 Jar 包,找不到的类会委托基座的类加载器去查找。
53+
54+
![图片](https://img.alicdn.com/imgextra/i2/O1CN01d20KY71Yie4N431VQ_!!6000000003093-0-tps-1080-521.jpg)
55+
56+
基于这套类委托的加载机制,让基座和模块共用的 class、resource 和 Jar 包通通下沉到基座中,可以让模块构建产物非常小,从而使模块消耗的 Metaspace 非常少,基座上能安装的模块数量也更多,启动也更快。
57+
58+
其次,模块启动后 Spring 上下文中会创建很多对象。如果启用了模块热卸载,可能无法完全回收,且安装次数过多会造成 Old 区、Metaspace 区开销大,触发频繁 FullGC。所以需要控制单模块包大小 < 5MB,这样不替换或重启基座也能热部署热卸载数百次。
59+
60+
在模块瘦身后,能实现以下两个好处:
61+
62+
* 允许基座安装更多的模块数量,从而在合并部署场景下,​**进一步降低资源成本**​;在热部署热卸载场景下,不替换或重启基座就能热部署、热卸载模块更多次。
63+
***提高模块安装的速度**​,减少模块包大小,减少启动依赖,控制模块安装耗时 < 30秒,甚至 < 5秒。
64+
65+
## 模块瘦身原则
66+
67+
由上文模块瘦身原理可知,模块移除的依赖必须在基座中存在,否则模块会在运行期间出现 ClassNotFound、LinkageError 等错误。
68+
69+
因此,模块瘦身的原则是,**​在保证模块功能的前提下,将框架、中间件等通用的依赖包尽量放置到基座中,模块中复用基座的依赖。​**这样打出的模块包会更加轻量。如图:
70+
71+
![图片](https://img.alicdn.com/imgextra/i3/O1CN01L6sOf21jNXamjclFd_!!6000000004536-0-tps-1080-390.jpg)
72+
73+
## 关键一:可感知的基座运行时
74+
75+
在基座和模块协作紧密的情况下,模块应该在开发时就感知基座正使用的所有依赖,并按需引入需要的依赖,而无需指定版本。为此,我们提供了 “基座-dependencies-starter” 的打包功能,该包在中记录了基座当前所有运行时依赖的 GAV 坐标 *(GAV: GroupId、ArtifactId、Version)*。打包方式非常简单,在基座的打包插件中配置必要的参数即可:
76+
77+
```bash
78+
<build>
79+
 <plugins>
80+
   <plugin>
81+
     <groupId>com.alipay.sofa.koupleless</groupId>
82+
     <artifactId>koupleless-base-build-plugin</artifactId>
83+
     <configuration>
84+
       <!-- ... -->
85+
       <!--生成 starter 的 artifactId(groupId和基座一致),这里需要修改!!-->
86+
       <dependencyArtifactId>${baseAppName}-dependencies-starter</dependencyArtifactId>
87+
       <!--生成jar的版本号-->
88+
       <dependencyVersion>0.0.1-SNAPSHOT</dependencyVersion>
89+
     </configuration>
90+
   </plugin>
91+
 </plugins>
92+
</build>
93+
```
94+
95+
执行 mvn 命令:
96+
97+
```bash
98+
mvn com.alipay.sofa.koupleless:koupleless-base-build-plugin::packageDependency -f ${基座 bootstrap pom 对于基座根目录的相对路径}
99+
```
100+
101+
然后,模块配置项目的 parent 为 “基座-dependencies-starter”。
102+
103+
```bash
104+
<parent>
105+
   <groupId>com.alipay</groupId>
106+
   <artifactId>${baseAppName}-dependencies-starter</artifactId>
107+
   <version>0.0.1</version>
108+
</parent>
109+
```
110+
111+
这样一来,在模块的开发过程中,开发者就能感知到基座运行时的所有依赖。
112+
113+
## ​关键二:​低成本的模块瘦身
114+
115+
在应用中,最简单的移除依赖的方式是把依赖的 scope 设置为 provided。但这种方式会增加普通应用转换为模块的成本,同时也意味着,如果模块要转为普通应用,需要将这些依赖配置回 compile,改造成本较高。
116+
117+
为了降低模块瘦身的成本,我们提供了两种配置模块瘦身的方式:基于 “基座-dependencies-starter” 自动瘦身和基于配置文件瘦身。
118+
119+
### 基于 “基座-dependencies-starter” 自动瘦身
120+
121+
我们提供了基于 “基座-dependencies-starter” 的自动瘦身,自动排除和基座相同的依赖​*(GAV 都相同)*​,保留和基座不同的依赖。配置十分简单,在模块的打包插件中配置 baseDependencyParentIdentity 标识即可:
122+
123+
```bash
124+
<build>
125+
 <plugins>
126+
   <plugin>
127+
     <groupId>com.alipay.sofa</groupId>
128+
     <artifactId>sofa-ark-maven-plugin</artifactId>
129+
     <configuration>
130+
       <!-- ... -->
131+
       <!-- 配置 “基座-dependencies-starter” 的标识,规范为:'${groupId}:${artifactId}' -->
132+
       <baseDependencyParentIdentity>${groupId}:${baseAppName}-dependencies-starter</baseDependencyParentIdentity>
133+
     </configuration>
134+
   </plugin>
135+
 </plugins>
136+
</build>
137+
```
138+
139+
### 基于配置文件瘦身
140+
141+
在配置文件中,模块开发者可以主动配置需要排除哪些依赖,保留哪些依赖。
142+
143+
为了进一步降低配置成本,用户仅需配置需要排除的顶层依赖,打包插件会将该顶层依赖的所有间接依赖都排除,而无需手动配置所有的间接依赖。如:
144+
145+
```bash
146+
# excludes config ${groupId}:{artifactId}:{version}, split by ','
147+
excludes=org.apache.commons:commons-lang3,commons-beanutils:commons-beanutils
148+
# excludeGroupIds config ${groupId}, split by ','
149+
excludeGroupIds=org.springframework
150+
# excludeArtifactIds config ${artifactId}, split by ','
151+
excludeArtifactIds=sofa-ark-spi
152+
```
153+
154+
### ​关键三:​保证瘦身的正确性
155+
156+
如果基座运行时没有模块被排除的依赖,或者基座运行时中提供的依赖版本和模块预期不一致,那么模块运行过程中可能会报错。为了保证瘦身的正确性,我们需要在**模块编译和发布**的环节做检查。
157+
158+
在模块编译时,模块打包插件会检查被瘦身的依赖是否在 “基座-dependencies-starter” 中,并在控制台输出检查结果,但检查结果不影响模块的构建结果。同时,插件允许更严格的检查:配置一定参数。如果基座中不存在模块被排除的依赖,那么模块构建失败,直接报错。
159+
160+
在模块发布时,在发布流程中拉取基座的运行时依赖,检查是否和 “基座-dependencies-starter” 一致。如果不一致,那么卡住发布流程,开发者可根据情况去升级模块的 “基座-dependencies-starter” 或跳过该卡点。
161+
162+
## 模块瘦身效果
163+
164+
以某个依赖了 16 个中间件的模块为例,将模块的 parent 配置为 “基座-dependencies-starter” 自动瘦身,下表是瘦身前后的 ark-biz.jar 大小和 Metaspace 占用的对比:
165+
166+
![图片](https://img.alicdn.com/imgextra/i1/O1CN01JTGK8z1S7BzCySALM_!!6000000002199-0-tps-1080-188.jpg)
167+
168+
## 总结
169+
170+
通过上文相信大家已经了解,我们可以通过简单的配置,让模块打包更小,从而在一个基座上安装更多的 Koupleless 模块,进一步降低资源成本。
171+
172+
最后,再次欢迎大家使用 Koupleless 和参与共建,我们期待您宝贵的意见!

0 commit comments

Comments
 (0)