模块化开发框架实现原理
本文是蚂蚁金服微贷事业部自主研发的模块化开发框架TITAN的实现原理,该框架后续可能会开源,敬请期待!
需求背景
- 应用拆分的多或少都有问题。多则维护成本高,每次发布一堆应用。少则拆分成本高,无用功能很难下线。
- 故障不隔离。当一个系统由多人同时参与开发时,修改A功能,可能会影响B功能,引发故障。
- 多分支开发引发冲突。多分支开发完之后合并会产生冲突。
- 牵一发动全身。一处核心代码的改动,或一个基础Jar的升级需要回归整个系统。
- 升级和迁移成本高。中间件升级每个应用都有升级成本。
模块化开发的好处
- 可插拔,一个应用由多个模块组成,应用里的模块可拆和合,模块可快速在多个系统中迁移和部署。
- 模块化开发,模块之间互相隔离,实现故障隔离。
- 一个模块一个分支,不会引发代码冲突。
- 在模块中增加或修改功能,只会影响当前模块,不会影响整个应用。
- 动态部署,在运行时把模块部署到应用中,快速修复故障,提高发布效率。
- 多版本部署,可以在运行时同时部署某个模块的新旧版本,进行AB TEST。
- 减少资源消耗,通过部署模块的方式减少应用数量和机器数量。
TITAN的应用场景
模块化开发框架适用于一些易变的系统,需要经常在一个系统中开发需求,比如如下场景
- 数据采集,数据采集的数据源多,而且每种数据源都需要对接和开发,且数据质量很差,需要经常调整解析代码,通过模块化开发,实现一个数据源使用一个模块进行对接。
- 指标计算,每种消息一个模块,把消息转发到模块中进行消息处理。
- 后台管理系统,几乎每个系统都有后台开发的需求,新建应用则应用数多,维护成本高,引入模块化开发,一个二级域一个模块来开发后台功能。
- 业务核对系统,需要不停的在线上增加核对代码,每个系统一个核对模块。
要实现模块化开发,需要实现几个功能,如何实现模块的加载和卸载,如何实现模块间的隔离和通讯,和模块的类加载机制。
模块加载
TITAN为每个模块创建一个新的URLClassLoader来加载模块。并且支持突破双亲委派,设置了overridePackages的包将由子类加载进行加载,不优先使用父类加载器已加载的。
模块的卸载
卸载模块需要满足三个条件
- 模块里的实例对象没有被引用
- 模块里的Class没有被引用
- 类加载器没有被引用
所以需要做到三点卸载实例,卸载类和卸载类加载器,整个模块的卸载顺序如下:
- 关闭资源:关闭HTTP连接池或线程池。
- 关闭IOC容器:调用applicationContext.close()方法关闭IOC容器。
- 移除类加载器:去掉模块的引用。
- 卸载JVM租户(开发中):卸载该模块使用的JVM租户,释放资源。
模块间隔离
模块化开发需要解决隔离性问题,否则各模块之间会互相影响。模块之间的隔离有三个层次:
- 类隔离:为每个模块创建一个类加载器来实现类隔离。
- 实例隔离:为每个模块创建一个新的IOC容器来加载模块里面的BEAN。
- 资源隔离:对每个模块只能使用指定的CPU和内存。
目前TITAN实现了类隔离和实例隔离,资源隔离准备引入JVM多租户来解决。
模块间通讯
模块之间的通讯也有三种方式,RPC,本地调用,深克隆/反射。
- 本地调用:目前TITAN的doAction就是使用的这种通讯方式,这种方式要求通讯的两个模块都在一个JVM里,两个模块的类加载器是父子关系,并且两个模块的IOC容器也是父子容器。
- RPC调用:用于跨JVM的模块之间调用,可以在D模块中发布服务,C模块引用RPC服务。
- 深克隆/反射:深克隆其他模块的入参,反射其他模块的方法实现调用,要求两个模块在一个JVM下。
类加载机制
OSGI类加载机制的关系采用的是网状结构,每个模块通过 Export-Package
来声明我要给别人用哪些类,通过 Import-Package
来声明我要用别人的哪些类。而TITAN框架采用的是扁平化管理,每个模块都有一个共同的父类,这个父类加载器就是加载ModuleLoader类的加载器,好处是便于维护,每个模块的类做到充分隔离,缺点是会加载重复的Class,适用于模块较少的场景。
原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 模块化开发框架实现原理
赞!期待开源
TITAN 和java9 的模块化 有什么异同?