目录
Koupleless 简介
Koupleless 是一种模块化的 Serverless 技术解决方案,它能让普通应用以较低的代价演进为 Serverless 研发模式,让代码与资源解耦,轻松独立维护,同时支持秒级构建部署、合并部署、动态伸缩等能力,为用户提供极致的研发运维体验。
适用场景
- 应用构建发布慢或 SDK 升级繁琐:传统应用构建发布需要 6-10 分钟,使用 Koupless 可降至 10 秒级
- 长尾应用资源浪费:企业 80% 的应用 CPU 使用率低于 10%,合并部署可显著降低资源成本
- 研发协作效率低:多人开发一个应用时,需要统一时间窗口发布, Koupless 支持模块独立迭代
- 中台应用难以沉淀业务资产:通过基座沉淀公共能力,模块实现具体业务逻辑
- 微服务演进成本高:支持应用在单体、模块化、微服务架构间平滑过渡
合并部署概述
什么是合并部署
合并部署是指将多个独立的应用(在 Koupless 中称为"模块")部署到同一个 JVM 进程中,共享基座的资源和依赖,但保持代码和运行时的隔离。
合并部署的优势
- 节省资源:多个模块共享基座的内存(Metaspace 和 Heap),CPU 使用率有效提升
- 快速启动:模块构建产物从数百 MB 瘦身到几十 MB,启动时间大幅缩短
- 简化运维:统一管理多个模块,降低维护成本
- 灵活扩展:支持动态添加、移除模块,无需重启基座
架构原理
Koupleless 基于 SOFAArk 框架实现类隔离和合并部署:
- 基座(Base):提供公共依赖和基础能力,如 SpringBoot 框架、中间件 SDK 等
- 模块(Biz):业务功能模块,依赖基座提供的公共能力
- 类加载机制:模块优先从自己的 ClassLoader 查找类,找不到再委托给基座 ClassLoader
环境准备
前置条件
- JDK 8 或更高版本
- Maven 3.6+
- SpringBoot 2.x 或 3.x
- 可用的 IDE(IntelliJ IDEA、Eclipse 等)
版本选择
当前 Koupleless 主要版本:
工具准备
下载 Arkctl 工具(用于模块部署):
基座改造
1. 添加依赖
在基座的 pom.xml 中添加 Koupleless 基座依赖:
2. 配置应用名
在 application.properties 或 application.yml 中配置应用名:
3. 配置基座构建插件
在 pom.xml 的 build 部分添加基座构建插件:
4. 生成依赖 Starter
执行以下命令生成基座的依赖 starter:
这个命令会生成一个 ${baseAppName}-dependencies-starter 的依赖包,模块项目可以将其作为 parent 来继承基座的所有依赖。
5. 启动基座
完成上述配置后,可以直接通过 IDE 或命令行启动基座:
模块改造
1. 创建模块项目
模块可以是现有的 SpringBoot 应用,也可以是新创建的项目。建议使用 Maven 多模块结构管理多个模块。
2. 配置模块依赖
在模块的 pom.xml 中添加模块依赖和打包插件:
3. 配置打包插件
在模块的 pom.xml 中添加 SOFAArk 打包插件:
4. 配置模块瘦身
下载自动排包配置文件 rules.txt,放在模块的 conf/ark/ 目录下:
rules.txt 内容示例:
5. 开发模块代码
创建模块的业务代码,例如 REST Controller:
6. 配置应用名
在模块的 application.properties 中配置应用名:
模块瘦身
为什么要模块瘦身
模块瘦身可以:
- 减小模块包大小:从数百 MB 降低到几十 MB
- 减少内存占用:降低 Metaspace 占用,基座可以安装更多模块
- 加快启动速度:模块启动时间从分钟级降低到秒级
- 提高部署效率:小体积包更容易传输和部署
瘦身原理
模块瘦身的核心思想是:移除模块中与基座重复的依赖,让模块复用基座的类
通过以下方式实现:
- 继承基座依赖 starter:模块以
${baseAppName}-dependencies-starter作为 parent - 自动排包:使用
rules.txt配置文件排除不需要的依赖 - 依赖委托:模块依赖设置为
provided作用域,委托给基座加载
配置方法
方法一:继承基座依赖 starter(推荐)
在模块的 pom.xml 中配置:
这样模块会自动继承基座的所有依赖,无需手动配置版本。
方法二:手动配置依赖作用域
在模块的 pom.xml 中,将基座已有的依赖设置为 provided 作用域:
方法三:使用排除配置
在 sofa-ark-maven-plugin 中配置排除项:
验证瘦身效果
构建模块并查看包大小:
可以看到,瘦身后的 ark-biz.jar 从 180M 降低到 15M,效果显著。
部署与验证
1. 启动基座
首先启动基座应用:
或使用 IDE 启动主类。
2. 构建模块
在模块项目中执行构建:
构建成功后会生成两个 jar 包:
module1-1.0.0.jar:普通 SpringBoot fat jar,可独立运行module1-1.0.0-ark-biz.jar:Koupleless 模块包,用于合并部署
3. 使用 Arkctl 部署模块
使用 Arkctl 工具将模块部署到基座:
4. 验证部署
部署成功后,可以通过以下方式验证:
查看模块状态
输出示例:
访问模块接口
查看日志
在基座控制台可以看到模块启动日志:
5. 部署多个模块
重复上述步骤部署 module2:
验证多模块部署:
6. 卸载模块
常见问题与解决方案
1. 类冲突问题
问题描述:模块和基座存在相同类名的类,导致类冲突。
解决方案:
- 使用类隔离机制,模块优先加载自己的类
- 公共类下沉到基座,模块通过依赖委托复用
- 避免在模块和基座中定义相同包名和类名的类
2. 静态字段共享问题
问题描述:多个模块共享静态字段,导致数据混乱。
解决方案:
使用 StaticFieldMapWrapper 适配静态字段:
3. Dubbo 兼容性问题
问题描述:模块中使用 Dubbo 泛化调用时出现问题。
解决方案:
- 确保 Dubbo 相关依赖下沉到基座
- 为 Dubbo 配置正确的 ClassLoader 切换
- 参考官方 adapter 仓库的解决方案
4. Nacos 线程过多
问题描述:每个模块启动时创建 Nacos 客户端,导致线程数过多。
解决方案:
在基座中缓存 Nacos 客户端:
5. 模块启动顺序问题
问题描述:模块之间有依赖关系,需要按顺序启动。
解决方案:
- 在基座中配置模块启动顺序
- 使用依赖关系控制启动顺序
- 避免循环依赖
6. 内存泄漏
问题描述:模块卸载后,内存没有释放,导致 Metaspace 持续增长。
解决方案:
- 确保模块卸载时清理所有资源(线程池、连接等)
- 使用弱引用管理缓存
- 定期重启基座或进行内存整理
7. Spring 上下文冲突
问题描述:模块和基座的 Spring Bean 冲突。
解决方案:
- 使用不同的 Bean 名称
- 模块中使用
@Primary注解明确优先级 - 避免模块和基座定义相同的 Bean ---
生产环境部署
1. 部署架构
生产环境推荐部署架构:
2. K8s 部署配置
基座 Deployment
模块发布流水线
3. 模块动态部署
方案一:OSS 监听模式
基座监听 OSS 目录变化,自动部署新模块:
方案二:配置中心模式
通过配置中心控制模块部署:
基座监听配置变化:
下面把教程里没展开的两件事一次讲透:
- 静态合并部署(Static Packaged Deployment)——一次性把基座 + N 个模块打成一个可执行 Fat Jar,启动即“全员就位”;
- 端口隔离——让每个模块真的监听自己的端口,实现“进程内多 WebServer”。
------------------------------------------------
方案三、静态合并部署(Static Packaged Deployment)
适用场景
- 上线流程极简,不允许运维侧再敲 arkctl deploy;
- 容器镜像只启一个进程,K8s liveness/readiness 探针直接探测基座即可;
- 模块版本固定,不需要热卸载/热加载。
打包思路
基座 pom 里把各模块的 ark-biz.jar 当资源一起打进 BOOT-INF/lib 目录;
基座启动时借助 SOFAArk 的 “static-biz” 机制,让这些 ark-biz.jar 随基座生命周期一起 install + start。实操步骤
step-1 模块端正常 mvn package 得到 moduleX-1.0.0-ark-biz.jar
step-2 基座 pom 增加 profile(只在打静态包时激活)
step-3 基座启动类加 @StaticBiz 注解(0.5.6+ 支持),告诉 SOFAArk 把 classpath 下所有 ark-biz.jar 静态安装:
step-4 打静态包
生成的 base-application-1.0.0-exec.jar 里同时包含 module1-ark-biz.jar & module2-ark-biz.jar;
java -jar base-application-1.0.0-exec.jar 启动后,arkctl status 能看到两个模块已经是 ACTIVATED,无需再 deploy。
让模块监听自己的端口
默认行为
- 所有模块与基座共用同一个嵌入式 Tomcat(端口 8080),仅靠 webContextPath 区分;
- 好处:节省线程、节省内存;
- 坏处:无法“端口级”隔离,也做不到“模块单独暴露管理口”。
Koupleless 0.5.6 开始支持 “多 WebServer 模式”,即每个模块可以起独立的 Netty/Tomcat,真正 bind 自己的端口。
- 模块端增加依赖
- 配置端口 & 上下文
- 打包插件声明
- 部署验证
基座仍跑 8080,module1 会额外监听 8081,module2 可再配 8082,互不影响;
curl http://localhost:8081/ 直接返回模块 1 的响应,无需再加前缀。
注意
- 端口多占 n 个,内存/线程也会相应增加,按需权衡;
- 生产环境需在 K8s Service 里把 8081、8082… 也声明出来,或走 Istio 多端口即可。
参考资源
- 官方文档:https://koupleless.io/docs/
- GitHub 仓库:https://github.com/koupleless/koupleless
- 示例代码:https://github.com/koupleless/samples
- 社区支持:https://github.com/koupleless/koupleless/issues