阿里再开源!基于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

本文作者:清英

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

相关文章
|
6天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
12天前
|
SQL 监控 数据可视化
完全开源!国内首个完全开源JAVA企业级低代码平台
JeeLowCode 是一款专为企业打造的 Java 企业级低代码开发平台,通过五大核心引擎(SQL、功能、模板、图表、切面)和四大服务体系(开发、设计、图表、模版),简化开发流程,降低技术门槛,提高研发效率。平台支持多端适配、国际化、事件绑定与动态交互等功能,广泛适用于 OA、ERP、IoT 等多种管理信息系统,帮助企业加速数字化转型。
|
15天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
15天前
|
消息中间件 Java 数据库连接
Java 反射最全详解 ,框架设计必掌握!
本文详细解析Java反射机制,包括反射的概念、用途、实现原理及应用场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Java 反射最全详解 ,框架设计必掌握!
|
21天前
|
SQL Java 关系型数据库
java连接mysql查询数据(基础版,无框架)
【10月更文挑战第12天】该示例展示了如何使用Java通过JDBC连接MySQL数据库并查询数据。首先在项目中引入`mysql-connector-java`依赖,然后通过`JdbcUtil`类中的`main`方法实现数据库连接、执行SQL查询及结果处理,最后关闭相关资源。
|
17天前
|
缓存 Java 数据库连接
Hibernate:Java持久层框架的高效应用
通过上述步骤,可以在Java项目中高效应用Hibernate框架,实现对关系数据库的透明持久化管理。Hibernate提供的强大功能和灵活配置,使得开发者能够专注于业务逻辑的实现,而不必过多关注底层数据库操作。
11 1
|
23天前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
18天前
|
SQL 监控 数据可视化
完全开源!国内首个完全开源JAVA企业级低代码平台
JeeLowCode 是一款专为企业打造的 Java 企业级低代码开发平台,通过五大核心引擎(SQL、功能、模板、图表、切面)和四大服务体系(开发、设计、图表、模版),简化开发流程,降低技术门槛,提高研发效率。平台支持多端适配、国际化、事件绑定与动态交互等功能,广泛适用于 OA、ERP、IoT 等多种管理信息系统,帮助企业加速数字化转型。
完全开源!国内首个完全开源JAVA企业级低代码平台
|
10天前
|
存储 Java 开发者
Java中的集合框架深入解析
【10月更文挑战第32天】本文旨在为读者揭开Java集合框架的神秘面纱,通过深入浅出的方式介绍其内部结构与运作机制。我们将从集合框架的设计哲学出发,探讨其如何影响我们的编程实践,并配以代码示例,展示如何在真实场景中应用这些知识。无论你是Java新手还是资深开发者,这篇文章都将为你提供新的视角和实用技巧。
11 0
|
4月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
62 1