阿里再开源!基于JAVA的模块化开发框架JarsLink

简介:

需求背景

8481c8f592b7f349aa84a1de5c171db681516edf 应用拆分的多或少都有问题。多则维护成本高,每次发布一堆应用。少则拆分成本高,无用功能很难下线。
8481c8f592b7f349aa84a1de5c171db681516edf 故障不隔离。当一个系统由多人同时参与开发时,修改A功能,可能会影响B功能,引发故障。
8481c8f592b7f349aa84a1de5c171db681516edf 多分支开发引发冲突。多分支开发完之后合并会产生冲突。
8481c8f592b7f349aa84a1de5c171db681516edf 牵一发动全身。一处核心代码的改动,或一个基础Jar的升级需要回归整个系统。
8481c8f592b7f349aa84a1de5c171db681516edf 升级和迁移成本高。中间件升级每个应用都有升级成本。

模块化开发的好处

8cad557d4753b1803698c955b6ce5bdd0eb64cc8

8481c8f592b7f349aa84a1de5c171db681516edf 可插拔,一个应用由多个模块组成,应用里的模块可拆和合,模块可快速在多个系统中迁移和部署。
8481c8f592b7f349aa84a1de5c171db681516edf 模块化开发,模块之间互相隔离,实现故障隔离。
8481c8f592b7f349aa84a1de5c171db681516edf 一个模块一个分支,不会引发代码冲突。
8481c8f592b7f349aa84a1de5c171db681516edf 在模块中增加或修改功能,只会影响当前模块,不会影响整个应用。
8481c8f592b7f349aa84a1de5c171db681516edf 动态部署,在运行时把模块部署到应用中,快速修复故障,提高发布效率。
8481c8f592b7f349aa84a1de5c171db681516edf 多版本部署,可以在运行时同时部署某个模块的新旧版本,进行AB TEST。
8481c8f592b7f349aa84a1de5c171db681516edf 减少资源消耗,通过部署模块的方式减少应用数量和机器数量。

JarsLink的应用场景

8481c8f592b7f349aa84a1de5c171db681516edf 数据管理中心,如果你需要开发一个数据管理系统,这个系统需要去不同的异构系统采集数据,这些系统会提供不同类型的接口,如RPC,HTTP等。并且数据采集的数据源多,每种数据源都需要对接和开发,数据质量比较差,需要经常修改代码进行发布。在这种场景下,通过模块化开发,实现一个数据源使用一个模块进行对接,上线新数据源只需要新增模块,修改BUG只需要修改某个模块,并能快速上线。
8481c8f592b7f349aa84a1de5c171db681516edf 后台管理系统,互联网应用发展到一定阶段会出现很多后台需求,如客服查询用户的信息帮助解答问题,开发查后台数据排查系统BUG,运营使用后台功能发送运营活动等。这些功能发布频率会大于核心系统,如果放在核心系统里会影响其稳定性,所以我们必须要建一个后台系统来开发后台功能,但是这样又带来一个新的问题,很多开发都会来这个系统进行开发,拉多分支造成代码冲突,A业务的BUG影响到B业务。所以如果每个业务线一个模块,每个模块使用一个单独的分支进行开发,就能进行隔离开发,提高开发速度,开发完后在运行时加载到系统中。
8481c8f592b7f349aa84a1de5c171db681516edf 微服务集成测试, 目前一个微服务是一个FAT JAR,如果有几十个微服务,则需要启动很多进程,DEBUG端口会很多,使用JarsLink框架合并FAT JAR,再路由请求到其他JAR,就可以只启动一个进程进行DEBUG测试。
8481c8f592b7f349aa84a1de5c171db681516edf 指标计算系统,可以把消息转发到不同的模块中进行处理,并输出指标。

目前蚂蚁金服微贷事业部几个系统和几十个模块已经使用JarsLink框架。

JarsLink的特性

隔离性

8481c8f592b7f349aa84a1de5c171db681516edf 类隔离:框架为每个模块的Class使用单独的ClassLoader来加载,每个模块可以依赖同一种框架的不同的版本。
8481c8f592b7f349aa84a1de5c171db681516edf 实例隔离:框架为每个模块创建了一个独立的Spring上下文,来加载模块中的BEAN,实例化失败不会影响其他模块。
8481c8f592b7f349aa84a1de5c171db681516edf 资源隔离:后续会支持模块之间的资源隔离,每个模块使用独立的CPU和内存资源。

动态性

8481c8f592b7f349aa84a1de5c171db681516edf 动态发布:模块能在运行时动态加载到系统中,实现不需要重启和发布系统新增功能。支持突破双亲委派机制,在运行时加载父加载器已经加载过的类,实现模块升级依赖包不需要系统发布。
8481c8f592b7f349aa84a1de5c171db681516edf 动态卸载:模块能在运行时被动态卸载干净,实现快速下线不需要功能。

易用性

提供了通用灵活的API让系统和模块进行交互。

实现原理

模块加载

JarsLink为每个模块创建一个新的URLClassLoader来加载模块。并且支持突破双亲委派,设置了overridePackages的包将由子类加载进行加载,不优先使用父类加载器已加载的。

模块的卸载

卸载模块需要满足三个条件:

8481c8f592b7f349aa84a1de5c171db681516edf 模块里的实例对象没有被引用
8481c8f592b7f349aa84a1de5c171db681516edf 模块里的Class没有被引用
8481c8f592b7f349aa84a1de5c171db681516edf 类加载器没有被引用

所以需要做到三点卸载实例,卸载类和卸载类加载器,整个模块的卸载顺序如下:

36f23537363c7642fc76b5f0e7ad2ef2aab1855e
8481c8f592b7f349aa84a1de5c171db681516edf 关闭资源:关闭HTTP连接池或线程池。
8481c8f592b7f349aa84a1de5c171db681516edf 关闭IOC容器:调用applicationContext.close()方法关闭IOC容器。
8481c8f592b7f349aa84a1de5c171db681516edf 移除类加载器:去掉模块的引用。
8481c8f592b7f349aa84a1de5c171db681516edf 卸载JVM租户(开发中):卸载该模块使用的JVM租户,释放资源。

模块间隔离

模块化开发需要解决隔离性问题,否则各模块之间会互相影响。模块之间的隔离有三个层次:

8481c8f592b7f349aa84a1de5c171db681516edf 类隔离:为每个模块创建一个类加载器来实现类隔离。
8481c8f592b7f349aa84a1de5c171db681516edf 实例隔离:为每个模块创建一个新的IOC容器来加载模块里面的BEAN。
8481c8f592b7f349aa84a1de5c171db681516edf 资源隔离:对每个模块只能使用指定的CPU和内存。

目前JarsLink实现了类隔离和实例隔离,资源隔离准备引入ALIJVM多租户来解决。

ccbfd650e876205353ece8e646f230c09bfcf470

模块间通讯

模块之间的通讯也有三种方式,RPC,本地调用,深克隆/反射。

16e0be77f859a10e8aff8535362b0c8a9e50b1d6
8481c8f592b7f349aa84a1de5c171db681516edf 本地调用:目前JarsLink的doAction就是使用的这种通讯方式,这种方式要求模块的类加载器是父子关系,且IOC容器也是父子容器。
8481c8f592b7f349aa84a1de5c171db681516edf RPC调用:用于跨JVM的模块之间调用,利用SOFA 4动态API在模块中发布和引用TR服务来实现。
8481c8f592b7f349aa84a1de5c171db681516edf 深克隆/反射:深克隆其他模块的入参,反射其他模块的方法实现调用。

类加载机制

OSGI类加载机制的关系采用的是网状结构,每个模块通过 Export-Package 来声明我要给别人用哪些类,通过 Import-Package来声明我要用别人的哪些类。JarsLink采用扁平化管理,每个模块都有一个共同的父类,这个父类加载器就是加载ModuleLoader类的加载器,如果是SOFA应用,模块的父加载器是KernelAceClassLoader,类加载器关系如下:

f00a76b838a0de70ed59de7287ba9dd62099d629

如果所有模块都需要使用的类,可以通过KernelAceClassLoader加载,如果是SOFA系统可以通过POM引入。

JarsLink框架类图

JarsLink框架的类图如下:

135c3797e899e483b110d92924ebcc0aab977dd7
8481c8f592b7f349aa84a1de5c171db681516edf AbstractModuleRefreshScheduler:入口类,负责定期扫描本地和内存中的模块是否发生变更,如果变更,则更新模块。
8481c8f592b7f349aa84a1de5c171db681516edf ModuleLoader:模块加载引擎,负责模块加载。
8481c8f592b7f349aa84a1de5c171db681516edf ModuleManager:模块管理者,负责在运行时注册,卸载,查找模块和执行Action。
8481c8f592b7f349aa84a1de5c171db681516edf Module:模块,一个模块有多个Action。
8481c8f592b7f349aa84a1de5c171db681516edf Action:模块里的执行者。

如何使用?

1:引入POM

JarsLink Maven Repo

abc9950ab261e800f09a282453d59c1d8a0eb0c4

JarsLink依赖的POM也需要引入

55507de14bce608f2d02c118c47b367b4b111927

2:引入jarslink BEAN

在系统中引入以下两个BEAN。

2b0df9366927ef60ecceffc4d58437acdf0972dd

3:集成JarsLink API

使用JarsLink API非常简单,只需要继承AbstractModuleRefreshScheduler,并提供模块的配置信息,代码如下:

1d30ccc76f16548a563a9be959a282045a5d394c

这个调度器在bean初始化的时候会启动一个调度任务,每分钟刷新一次模块,如果模块的版本号发生变更则会更新模块。实现这个方法时,必须把模块(jar包)下载到机器本地,模块的配置信息说明如下:

8481c8f592b7f349aa84a1de5c171db681516edf name:全局唯一,建议使用英文,忽略大小写。
8481c8f592b7f349aa84a1de5c171db681516edf enabled:当前模块是否可用,默认可用,卸载模块时可以设置成false。
8481c8f592b7f349aa84a1de5c171db681516edf version:模块的版本,如果版本号和之前加载的不一致,框架则会重新加载模块。
8481c8f592b7f349aa84a1de5c171db681516edf Properties:spring属性配置文件。
8481c8f592b7f349aa84a1de5c171db681516edf moduleUrl:模块的本地存放地址。
8481c8f592b7f349aa84a1de5c171db681516edf overridePackages:需要突破双亲委派的包名,一般不推荐使用,范围越小越好,如com.alipay.XX。

把ModuleRefreshSchedulerImpl类注册成Spring的bean。

b5d16d52b00a73e30097dc5070e49900e90d5056

JarsLink API 暂时不提供模块可视化管理能力,所以需要使用其他系统来管理和发布模块。目前可以通过com.alipay. jarslink.api.ModuleManager#getModules获取运行时所有模块的信息。

你也可以使用API来加载并注册模块,详细使用方式可以参考ModuleManagerTest,代码如下。

25e1e61ed1a83e628912926764eb6892589b322c

3:开发模块

在模块中只需要实现并开发Action,代码如下:

0db6e3e4d43cf17fa1d31858ded4fe4e01c9655d

5:调用接口

开发者需要利用JarsLink API把请求转发给模块,先根据模块名查找模块,再根据aciton name查找Action,最后执行Action。

f20b75b4c0fe64dad162fc5ead7e262199626503

其他特性

Spring配置

通过moduleConfig的Properties属性可以设置Spring bean变量的配置信息。

02f6615a26da674a99c9f3e61596e6ee2920244e

最佳实践

HTTP请求转发

可以把HTTP请求转发给模块处理。

 

消息请求转发

可以把消息转发给模块进行处理。遵循默认大于配置的方式,你可以把TOPIC当做模块名,EventCode当做ActionName来转发请求。

接口说明

JarsLink框架最重要的两个接口是ModuleManager和ModuleLoader。

ModuleManager接口

ModuleManager负责注册,卸载,查找模块和执行Action。

15140a1db7cb47966ec09e18d25c838d4313d442

ModuleLoader接口

ModuleLoader只负责加载模块。

b101fac69bcbf50a720ef99dad26a6bf7dee8649

近期,JarsLink会支持多版本加载,并陆续支持模块间调用、资源隔离等特性。我们也希望更多的童鞋参与进来,让JarsLink帮助更多开发者提升效率。


原文发布时间为:2018-03-22

本文作者:清英

本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”微信公众号

相关文章
|
4月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
5月前
|
人工智能 Java 开发者
阿里出手!Java 开发者狂喜!开源 AI Agent 框架 JManus 来了,初次见面就心动~
JManus是阿里开源的Java版OpenManus,基于Spring AI Alibaba框架,助力Java开发者便捷应用AI技术。支持多Agent框架、网页配置、MCP协议及PLAN-ACT模式,可集成多模型,适配阿里云百炼平台与本地ollama。提供Docker与源码部署方式,具备无限上下文处理能力,适用于复杂AI场景。当前仍在完善模型配置等功能,欢迎参与开源共建。
2332 58
阿里出手!Java 开发者狂喜!开源 AI Agent 框架 JManus 来了,初次见面就心动~
|
4月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
259 8
|
4月前
|
存储 安全 Java
《数据之美》:Java集合框架全景解析
Java集合框架是数据管理的核心工具,涵盖List、Set、Map等体系,提供丰富接口与实现类,支持高效的数据操作与算法处理。
|
4月前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
145 7
|
5月前
|
消息中间件 人工智能 Java
抖音微信爆款小游戏大全:免费休闲/竞技/益智/PHP+Java全筏开源开发
本文基于2025年最新行业数据,深入解析抖音/微信爆款小游戏的开发逻辑,重点讲解PHP+Java双引擎架构实战,涵盖技术选型、架构设计、性能优化与开源生态,提供完整开源工具链,助力开发者从理论到落地打造高留存、高并发的小游戏产品。
|
Java 开发工具 git
Java开发初级6.24.3
5.在Git使用过程中,进行Git配置的操作命令是哪个() A. config B. config -g C. config -a D. git config 相关知识点: 在git中,经常使用git config 命令用来配置git的配置文件,git配置级别主要有:仓库级别 local 【优先级最高】、用户级别 global【优先级次之】、系统级别 system【优先级最低】 正确答案:D 10.RDBMS是什么? A. Rela Database Management Systems B. Relational Database Management Systems C. Relation
236 0
|
SQL 前端开发 JavaScript
Java开发初级6.24.2
3.Java网站src/main/java目录保存的是什么资源? A. Java源代码文件 B. 测试代码 C. JavaScript、CSS等文件 D. 图片资源 正确答案:A 4.什么是索引Index? A. SQL数据库里的表管理工具 B. SQL数据库里的查询工具 C. SQL数据库里的目录工具 D. SQL数据库用来加速数据查询的特殊的数据结构 正确答案:D
315 0
|
Java
Java开发初级6.24.1
1.下面关于泛型的描述中错误的一项是? A. “? extends 类”表示设置泛型上限 B. “? super 类”表示设置泛型下限 C. 利用“?”通配符可以接收全部的泛型类型实例,但却不可修改泛型属性内容 D. 如果类在定义时使用了泛型,则在实例化类对象时需要设置相应的泛型类型,否则程序将无法编译通过 相关知识点: https://edu.aliyun.com/course/35 正确答案:D 2.下列选项中属于SVN中控制鉴权用户访问版本库的权限默认权限的是() A. write B. read C. none D. null 相关知识点: auth-access:取值范围为"writ
352 0
|
Java 开发工具 git
Java开发初级6.23.3
5.在Git使用过程中,进行Git配置的操作命令是哪个() A. config B. config -g C. config -a D. git config 相关知识点: 在git中,经常使用git config 命令用来配置git的配置文件,git配置级别主要有:仓库级别 local 【优先级最高】、用户级别 global【优先级次之】、系统级别 system【优先级最低】 正确答案:D 10.RDBMS是什么? A. Rela Database Management Systems B. Relational Database Management Systems C. Relation
197 0