導(dǎo)讀:JarsLink是一個(gè)基于JAVA的模塊化開發(fā)框架型豁,它提供在運(yùn)行時(shí)動(dòng)態(tài)加載模塊(JAR包)、卸載模塊和模塊間調(diào)用的API尚蝌,它能夠幫助你進(jìn)行模塊化開發(fā)迎变,也能幫助你的系統(tǒng)在運(yùn)行時(shí)動(dòng)態(tài)添加新功能,減少編譯飘言、打包和部署帶來的發(fā)布耗時(shí)衣形,同時(shí)它也是阿里巴巴的開源項(xiàng)目之一,目前在螞蟻金服微貸事業(yè)群各團(tuán)隊(duì)廣泛使用姿鸿。
開源地址:https://github.com/alibaba/jarslink
需求背景
應(yīng)用拆分的多或少都有問題谆吴。多則維護(hù)成本高,每次發(fā)布一堆應(yīng)用苛预。少則拆分成本高句狼,無用功能很難下線。
故障不隔離热某。當(dāng)一個(gè)系統(tǒng)由多人同時(shí)參與開發(fā)時(shí)腻菇,修改A功能胳螟,可能會(huì)影響B(tài)功能,引發(fā)故障筹吐。
多分支開發(fā)引發(fā)沖突糖耸。多分支開發(fā)完之后合并會(huì)產(chǎn)生沖突。
牽一發(fā)動(dòng)全身丘薛。一處核心代碼的改動(dòng)嘉竟,或一個(gè)基礎(chǔ)Jar的升級(jí)需要回歸整個(gè)系統(tǒng)。
升級(jí)和遷移成本高洋侨。中間件升級(jí)每個(gè)應(yīng)用都有升級(jí)成本舍扰。
模塊化開發(fā)的好處
可插拔,一個(gè)應(yīng)用由多個(gè)模塊組成凰兑,應(yīng)用里的模塊可拆和合妥粟,模塊可快速在多個(gè)系統(tǒng)中遷移和部署。
模塊化開發(fā)吏够,模塊之間互相隔離勾给,實(shí)現(xiàn)故障隔離。
一個(gè)模塊一個(gè)分支锅知,不會(huì)引發(fā)代碼沖突播急。
在模塊中增加或修改功能,只會(huì)影響當(dāng)前模塊售睹,不會(huì)影響整個(gè)應(yīng)用桩警。
動(dòng)態(tài)部署,在運(yùn)行時(shí)把模塊部署到應(yīng)用中昌妹,快速修復(fù)故障捶枢,提高發(fā)布效率。
多版本部署飞崖,可以在運(yùn)行時(shí)同時(shí)部署某個(gè)模塊的新舊版本烂叔,進(jìn)行AB TEST。
減少資源消耗固歪,通過部署模塊的方式減少應(yīng)用數(shù)量和機(jī)器數(shù)量蒜鸡。
JarsLink的應(yīng)用場(chǎng)景
數(shù)據(jù)管理中心,如果你需要開發(fā)一個(gè)數(shù)據(jù)管理系統(tǒng)牢裳,這個(gè)系統(tǒng)需要去不同的異構(gòu)系統(tǒng)采集數(shù)據(jù)逢防,這些系統(tǒng)會(huì)提供不同類型的接口,如RPC蒲讯,HTTP等忘朝。并且數(shù)據(jù)采集的數(shù)據(jù)源多,每種數(shù)據(jù)源都需要對(duì)接和開發(fā)伶椿,數(shù)據(jù)質(zhì)量比較差辜伟,需要經(jīng)常修改代碼進(jìn)行發(fā)布氓侧。在這種場(chǎng)景下,通過模塊化開發(fā)导狡,實(shí)現(xiàn)一個(gè)數(shù)據(jù)源使用一個(gè)模塊進(jìn)行對(duì)接约巷,上線新數(shù)據(jù)源只需要新增模塊,修改BUG只需要修改某個(gè)模塊旱捧,并能快速上線独郎。
后臺(tái)管理系統(tǒng),互聯(lián)網(wǎng)應(yīng)用發(fā)展到一定階段會(huì)出現(xiàn)很多后臺(tái)需求枚赡,如客服查詢用戶的信息幫助解答問題氓癌,開發(fā)查后臺(tái)數(shù)據(jù)排查系統(tǒng)BUG,運(yùn)營(yíng)使用后臺(tái)功能發(fā)送運(yùn)營(yíng)活動(dòng)等贫橙。這些功能發(fā)布頻率會(huì)大于核心系統(tǒng)贪婉,如果放在核心系統(tǒng)里會(huì)影響其穩(wěn)定性,所以我們必須要建一個(gè)后臺(tái)系統(tǒng)來開發(fā)后臺(tái)功能卢肃,但是這樣又帶來一個(gè)新的問題疲迂,很多開發(fā)都會(huì)來這個(gè)系統(tǒng)進(jìn)行開發(fā),拉多分支造成代碼沖突莫湘,A業(yè)務(wù)的BUG影響到B業(yè)務(wù)尤蒿。所以如果每個(gè)業(yè)務(wù)線一個(gè)模塊,每個(gè)模塊使用一個(gè)單獨(dú)的分支進(jìn)行開發(fā)幅垮,就能進(jìn)行隔離開發(fā)腰池,提高開發(fā)速度,開發(fā)完后在運(yùn)行時(shí)加載到系統(tǒng)中忙芒。
微服務(wù)集成測(cè)試, 目前一個(gè)微服務(wù)是一個(gè)FAT JAR,如果有幾十個(gè)微服務(wù),則需要啟動(dòng)很多進(jìn)程,DEBUG端口會(huì)很多,使用JarsLink框架合并FAT JAR,再路由請(qǐng)求到其他JAR,就可以只啟動(dòng)一個(gè)進(jìn)程進(jìn)行DEBUG測(cè)試示弓。
指標(biāo)計(jì)算系統(tǒng),可以把消息轉(zhuǎn)發(fā)到不同的模塊中進(jìn)行處理呵萨,并輸出指標(biāo)避乏。
目前螞蟻金服微貸事業(yè)部幾個(gè)系統(tǒng)和幾十個(gè)模塊已經(jīng)使用JarsLink框架。
JarsLink的特性
隔離性
類隔離:框架為每個(gè)模塊的Class使用單獨(dú)的ClassLoader來加載甘桑,每個(gè)模塊可以依賴同一種框架的不同的版本。
實(shí)例隔離:框架為每個(gè)模塊創(chuàng)建了一個(gè)獨(dú)立的Spring上下文歹叮,來加載模塊中的BEAN跑杭,實(shí)例化失敗不會(huì)影響其他模塊。
資源隔離:后續(xù)會(huì)支持模塊之間的資源隔離咆耿,每個(gè)模塊使用獨(dú)立的CPU和內(nèi)存資源德谅。
動(dòng)態(tài)性
動(dòng)態(tài)發(fā)布:模塊能在運(yùn)行時(shí)動(dòng)態(tài)加載到系統(tǒng)中,實(shí)現(xiàn)不需要重啟和發(fā)布系統(tǒng)新增功能萨螺。支持突破雙親委派機(jī)制窄做,在運(yùn)行時(shí)加載父加載器已經(jīng)加載過的類愧驱,實(shí)現(xiàn)模塊升級(jí)依賴包不需要系統(tǒng)發(fā)布。
動(dòng)態(tài)卸載:模塊能在運(yùn)行時(shí)被動(dòng)態(tài)卸載干凈椭盏,實(shí)現(xiàn)快速下線不需要功能。
易用性
提供了通用靈活的API讓系統(tǒng)和模塊進(jìn)行交互。
實(shí)現(xiàn)原理
模塊加載
JarsLink為每個(gè)模塊創(chuàng)建一個(gè)新的URLClassLoader來加載模塊滔灶。并且支持突破雙親委派瘦癌,設(shè)置了overridePackages的包將由子類加載進(jìn)行加載,不優(yōu)先使用父類加載器已加載的乌叶。
模塊的卸載
卸載模塊需要滿足三個(gè)條件:
模塊里的實(shí)例對(duì)象沒有被引用
模塊里的Class沒有被引用
類加載器沒有被引用
所以需要做到三點(diǎn)卸載實(shí)例盆偿,卸載類和卸載類加載器,整個(gè)模塊的卸載順序如下:
?關(guān)閉資源:關(guān)閉HTTP連接池或線程池准浴。
關(guān)閉IOC容器:調(diào)用applicationContext.close()方法關(guān)閉IOC容器事扭。
移除類加載器:去掉模塊的引用。
卸載JVM租戶(開發(fā)中):卸載該模塊使用的JVM租戶乐横,釋放資源求橄。
模塊間隔離
模塊化開發(fā)需要解決隔離性問題,否則各模塊之間會(huì)互相影響晰奖。模塊之間的隔離有三個(gè)層次:
類隔離:為每個(gè)模塊創(chuàng)建一個(gè)類加載器來實(shí)現(xiàn)類隔離谈撒。
實(shí)例隔離:為每個(gè)模塊創(chuàng)建一個(gè)新的IOC容器來加載模塊里面的BEAN。
資源隔離:對(duì)每個(gè)模塊只能使用指定的CPU和內(nèi)存匾南。
目前JarsLink實(shí)現(xiàn)了類隔離和實(shí)例隔離啃匿,資源隔離準(zhǔn)備引入ALIJVM多租戶來解決。
模塊間通訊
模塊之間的通訊也有三種方式蛆楞,RPC溯乒,本地調(diào)用,深克隆/反射豹爹。
本地調(diào)用:目前JarsLink的doAction就是使用的這種通訊方式裆悄,這種方式要求模塊的類加載器是父子關(guān)系,且IOC容器也是父子容器臂聋。
RPC調(diào)用:用于跨JVM的模塊之間調(diào)用光稼,利用SOFA 4動(dòng)態(tài)API在模塊中發(fā)布和引用TR服務(wù)來實(shí)現(xiàn)。
深克隆/反射:深克隆其他模塊的入?yún)⒑⒌龋瓷淦渌K的方法實(shí)現(xiàn)調(diào)用艾君。
類加載機(jī)制
OSGI類加載機(jī)制的關(guān)系采用的是網(wǎng)狀結(jié)構(gòu),每個(gè)模塊通過?Export-Package?來聲明我要給別人用哪些類肄方,通過?Import-Package來聲明我要用別人的哪些類冰垄。JarsLink采用扁平化管理,每個(gè)模塊都有一個(gè)共同的父類权她,這個(gè)父類加載器就是加載ModuleLoader類的加載器虹茶,如果是SOFA應(yīng)用逝薪,模塊的父加載器是KernelAceClassLoader,類加載器關(guān)系如下:
如果所有模塊都需要使用的類蝴罪,可以通過KernelAceClassLoader加載董济,如果是SOFA系統(tǒng)可以通過POM引入。
JarsLink框架類圖
JarsLink框架的類圖如下:
AbstractModuleRefreshScheduler:入口類洲炊,負(fù)責(zé)定期掃描本地和內(nèi)存中的模塊是否發(fā)生變更感局,如果變更,則更新模塊暂衡。
ModuleLoader:模塊加載引擎询微,負(fù)責(zé)模塊加載。
ModuleManager:模塊管理者狂巢,負(fù)責(zé)在運(yùn)行時(shí)注冊(cè)撑毛,卸載,查找模塊和執(zhí)行Action唧领。
Module:模塊藻雌,一個(gè)模塊有多個(gè)Action。
Action:模塊里的執(zhí)行者斩个。
如何使用胯杭?
1:引入POM
JarsLink Maven Repo
JarsLink依賴的POM也需要引入
2:引入jarslink BEAN
在系統(tǒng)中引入以下兩個(gè)BEAN。
3:集成JarsLink API
使用JarsLink API非常簡(jiǎn)單受啥,只需要繼承AbstractModuleRefreshScheduler做个,并提供模塊的配置信息,代碼如下:
這個(gè)調(diào)度器在bean初始化的時(shí)候會(huì)啟動(dòng)一個(gè)調(diào)度任務(wù)滚局,每分鐘刷新一次模塊居暖,如果模塊的版本號(hào)發(fā)生變更則會(huì)更新模塊。實(shí)現(xiàn)這個(gè)方法時(shí)藤肢,必須把模塊(jar包)下載到機(jī)器本地太闺,模塊的配置信息說明如下:
name:全局唯一,建議使用英文嘁圈,忽略大小寫省骂。
enabled:當(dāng)前模塊是否可用,默認(rèn)可用最住,卸載模塊時(shí)可以設(shè)置成false冀宴。
version:模塊的版本,如果版本號(hào)和之前加載的不一致温学,框架則會(huì)重新加載模塊。
Properties:spring屬性配置文件甚疟。
moduleUrl:模塊的本地存放地址仗岖。
overridePackages:需要突破雙親委派的包名,一般不推薦使用逃延,范圍越小越好,如com.alipay.XX轧拄。
把ModuleRefreshSchedulerImpl類注冊(cè)成Spring的bean揽祥。
JarsLink API 暫時(shí)不提供模塊可視化管理能力,所以需要使用其他系統(tǒng)來管理和發(fā)布模塊檩电。目前可以通過com.alipay. jarslink.api.ModuleManager#getModules獲取運(yùn)行時(shí)所有模塊的信息拄丰。
你也可以使用API來加載并注冊(cè)模塊,詳細(xì)使用方式可以參考ModuleManagerTest俐末,代碼如下料按。
3:開發(fā)模塊
在模塊中只需要實(shí)現(xiàn)并開發(fā)Action,代碼如下:
5:調(diào)用接口
開發(fā)者需要利用JarsLink API把請(qǐng)求轉(zhuǎn)發(fā)給模塊卓箫,先根據(jù)模塊名查找模塊载矿,再根據(jù)aciton name查找Action,最后執(zhí)行Action烹卒。
其他特性
Spring配置
通過moduleConfig的Properties屬性可以設(shè)置Spring bean變量的配置信息闷盔。
最佳實(shí)踐
HTTP請(qǐng)求轉(zhuǎn)發(fā)
可以把HTTP請(qǐng)求轉(zhuǎn)發(fā)給模塊處理。
消息請(qǐng)求轉(zhuǎn)發(fā)
可以把消息轉(zhuǎn)發(fā)給模塊進(jìn)行處理旅急。遵循默認(rèn)大于配置的方式逢勾,你可以把TOPIC當(dāng)做模塊名,EventCode當(dāng)做ActionName來轉(zhuǎn)發(fā)請(qǐng)求藐吮。
接口說明
JarsLink框架最重要的兩個(gè)接口是ModuleManager和ModuleLoader溺拱。
ModuleManager接口
ModuleManager負(fù)責(zé)注冊(cè),卸載炎码,查找模塊和執(zhí)行Action盟迟。
ModuleLoader接口
ModuleLoader只負(fù)責(zé)加載模塊。
近期潦闲,JarsLink會(huì)支持多版本加載攒菠,并陸續(xù)支持模塊間調(diào)用、資源隔離等特性歉闰。我們也希望更多的童鞋參與進(jìn)來辖众,讓JarsLink幫助更多開發(fā)者提升效率。
關(guān)注「技術(shù)邊城」 ??把握前沿技術(shù)脈搏