SOFA-Ark 動(dòng)態(tài)部署
SOFAArk介紹https://www.sofastack.tech/projects/sofa-boot/sofa-ark-readme/ SOFASTACK 引用文章
SOFAArk基本介紹還是引用官網(wǎng)的文章,概括的比較全面了,后面就是我自己基于SOFAArk動(dòng)態(tài)部署的源碼分析~
SOFAArk 是一款基于 Java 實(shí)現(xiàn)的輕量級(jí)類隔離容器,主要提供類隔離和應(yīng)用(模塊)合并部署能力,由螞蟻金服公司開(kāi)源貢獻(xiàn);
場(chǎng)景
包沖突
日常使用 Java 開(kāi)發(fā),常常會(huì)遇到包依賴沖突的問(wèn)題,尤其當(dāng)應(yīng)用變得臃腫龐大,包沖突的問(wèn)題也會(huì)變得更加棘手,導(dǎo)致各種各樣的報(bào)錯(cuò),例如 LinkageError, NoSuchMethodError 等;實(shí)際開(kāi)發(fā)中,可以采用多種方法來(lái)解決包沖突問(wèn)題,比較常見(jiàn)的是類似 Spring Boot 的做法,統(tǒng)一管理應(yīng)用所有依賴包的版本,保證這些三方包不存在依賴沖突;這種做法只能有效避免包沖突問(wèn)題,不能根本上解決包沖突的問(wèn)題;如果某個(gè)應(yīng)用的確需要在運(yùn)行時(shí)使用兩個(gè)相互沖突的包,例如 protobuf2 和 protobuf3,那么類似 Spring Boot 的做法依然解決不了問(wèn)題。
為了徹底解決包沖突的問(wèn)題,需要借助類隔離機(jī)制,使用不同的 ClassLoader 加載不同版本的三方依賴,進(jìn)而隔離包沖突問(wèn)題;OSGI 作為業(yè)內(nèi)最出名的類隔離框架,自然是可以被用于解決上述包沖突問(wèn)題,但是 OSGI 框架太過(guò)臃腫,功能繁雜;為了解決包沖突問(wèn)題,引入 OSGI 框架,有牛刀殺雞之嫌,且反而使工程變得更加復(fù)雜,不利于開(kāi)發(fā);
SOFAArk 采用輕量級(jí)的類隔離方案來(lái)解決日常經(jīng)常遇到的包沖突問(wèn)題,在螞蟻金服內(nèi)部服務(wù)于整個(gè) SOFABoot 技術(shù)體系,彌補(bǔ) Spring Boot 沒(méi)有的類隔離能力。SOFAArk 提出了一種特殊的包結(jié)構(gòu) – Ark Plugin,在遇到包沖突時(shí),用戶可以使用 Maven 插件將若干沖突包打包成 Plugin,運(yùn)行時(shí)由獨(dú)立的 PluginClassLoader 加載,從而解決包沖突。
假設(shè)如下場(chǎng)景,如果工程需要引入兩個(gè)三方包:A 和 B,但是 A 需要依賴版本號(hào)為 0.1 的 C 包,而恰好 B 需要依賴版本號(hào)為 0.2 的 C 包,且 C 包的這兩個(gè)版本無(wú)法兼容:

此時(shí),即可使用 SOFAArk 解決該依賴沖突問(wèn)題;只需要把 A 和版本為 0.1 的 C 包一起打包成一個(gè) Ark 插件,然后讓?xiě)?yīng)用工程引入該插件依賴即可;
合并部署
復(fù)雜項(xiàng)目通常需要跨團(tuán)隊(duì)協(xié)作開(kāi)發(fā),各自負(fù)責(zé)不同的組件,而眾所周知,協(xié)調(diào)跨團(tuán)隊(duì)合作開(kāi)發(fā)會(huì)遇到不少問(wèn)題;比如各自技術(shù)棧不統(tǒng)一導(dǎo)致的依賴沖突,又比如往同一個(gè) Git 倉(cāng)庫(kù)提交代碼常常導(dǎo)致 merge 沖突。因此,如果能讓每個(gè)團(tuán)隊(duì)將負(fù)責(zé)的功能組件當(dāng)成一個(gè)個(gè)單獨(dú)的應(yīng)用開(kāi)發(fā),運(yùn)行時(shí)合并部署,通過(guò)統(tǒng)一的編程界面交互,那么將極大的提升開(kāi)發(fā)效率及應(yīng)用可擴(kuò)展性。SOFAArk 提出了一種特殊的包結(jié)構(gòu) – Ark Biz,用戶可以使用 Maven 插件將應(yīng)用打包成 Biz,允許多 Biz 在 SOFAArk 容器之上合并部署,并通過(guò)統(tǒng)一的編程界面交互。
靜態(tài)合并部署
SOFAArk 提供了靜態(tài)合并部署能力,在開(kāi)發(fā)階段,應(yīng)用可以將其他應(yīng)用打成的 Biz 包通過(guò) Maven 依賴的方式引入,而當(dāng)自身被打成可執(zhí)行 Fat Jar 時(shí),可以將其他應(yīng)用 Biz 包一并打入,啟動(dòng)時(shí),則會(huì)根據(jù)優(yōu)先級(jí)依次啟動(dòng)各應(yīng)用。每個(gè) Biz 使用獨(dú)立的 BizClassLoader 加載,不需要考慮相互依賴沖突問(wèn)題,Biz 之間則通過(guò) SofaService/SofaReference JVM 服務(wù)進(jìn)行交互。
動(dòng)態(tài)合并部署
動(dòng)態(tài)合并部署區(qū)別于靜態(tài)合并部署最大的一點(diǎn)是,運(yùn)行時(shí)通過(guò) API 或者配置中心(Zookeeper)來(lái)控制 Biz 的部署和卸載。動(dòng)態(tài)合并部署的設(shè)計(jì)理念圖如下:

無(wú)論是靜態(tài)還是動(dòng)態(tài)合并部署都會(huì)有宿主應(yīng)用(master biz)的概念, 如果 Ark 包只打包了一個(gè) Biz,則該 Biz 默認(rèn)成為宿主應(yīng)用;如果 Ark 包打包了多個(gè) Biz 包,需要配置指定宿主應(yīng)用。宿主應(yīng)用不允許被卸載,一般而言,宿主應(yīng)用會(huì)作為流量入口的中臺(tái)系統(tǒng),具體的服務(wù)實(shí)現(xiàn)會(huì)放在不同的動(dòng)態(tài) Biz 中,供宿主應(yīng)用調(diào)用。宿主應(yīng)用可以使用 SOFAArk 提供的客戶端 API 實(shí)現(xiàn)動(dòng)態(tài)應(yīng)用的部署和卸載。除了 API, SOFAArk 提供了 Config Plugin,用于對(duì)接配置中心(目前支持 Zookeeper),運(yùn)行時(shí)接受動(dòng)態(tài)配置;Config Plugin 會(huì)解析下發(fā)的配置,控制動(dòng)態(tài)應(yīng)用的部署和卸載。
原理
SOFAArk 包含三個(gè)概念,Ark Container, Ark Plugin 和 Ark Biz; 運(yùn)行時(shí)邏輯結(jié)構(gòu)圖如下:


在介紹這三個(gè)概念之前,先介紹上述 Ark 包概念;Ark 包是滿足特定目錄格式要求的可運(yùn)行 Fat Jar,使用官方提供的 Maven 插件 sofa-ark-maven-plugin 可以將單個(gè)或多個(gè)應(yīng)用打包成標(biāo)準(zhǔn)格式的 Ark 包;使用 java -jar 命令即可在 SOFAArk 容器之上啟動(dòng)所有應(yīng)用;Ark 包通常包含 Ark Container、Ark Plugin 和 Ark Biz;以下我們針對(duì)這三個(gè)概念簡(jiǎn)單做下名詞解釋:
Ark Container: SOFAArk 容器,負(fù)責(zé)Ark包啟動(dòng)運(yùn)行時(shí)的管理;Ark Plugin和Ark Biz運(yùn)行在 SOFAArk 容器之上;容器具備管理插件和應(yīng)用的功能;容器啟動(dòng)成功后,會(huì)自動(dòng)解析 classpath 包含的Ark Plugin和Ark Biz依賴,完成隔離加載并按優(yōu)先級(jí)依次啟動(dòng)之;Ark Plugin: Ark 插件,滿足特定目錄格式要求的Fat Jar,使用官方提供的Maven插件sofa-ark-plugin-maven-plugin可以將一個(gè)或多個(gè)普通的Java jar打包成一個(gè)標(biāo)準(zhǔn)格式的Ark Plugin;Ark Plugin會(huì)包含一份配置文件,通常包括插件類導(dǎo)入導(dǎo)出配置、資源導(dǎo)入導(dǎo)出配置、插件啟動(dòng)優(yōu)先級(jí)等;運(yùn)行時(shí),SOFAArk 容器會(huì)使用獨(dú)立的PluginClassLoader加載插件,并根據(jù)插件配置構(gòu)建類加載索引表、資源加載索引表,使插件和插件之間、插件和應(yīng)用之間相互隔離;Ark Biz: Ark 應(yīng)用模塊,滿足特定目錄格式要求的Fat Jar,使用官方提供的Maven插件sofa-ark-maven-plugin可以將工程應(yīng)用打包成一個(gè)標(biāo)準(zhǔn)格式的Ark Biz;Ark Biz是工程應(yīng)用以及其依賴包的組織單元,包含應(yīng)用啟動(dòng)所需的所有依賴和配置;一個(gè) Ark 包中可以包含多個(gè)Ark Biz包,按優(yōu)先級(jí)依次啟動(dòng),Biz 之間通過(guò) JVM 服務(wù)交互;
運(yùn)行 Ark 包,Ark Container 優(yōu)先啟動(dòng),容器自動(dòng)解析 Ark 包中含有的 Ark Plugin 和 Ark Biz,并讀取他們的配置信息,構(gòu)建類和資源的加載索引表;然后使用獨(dú)立的 ClassLoader 加載并按優(yōu)先級(jí)配置依次啟動(dòng);需要指出的是,Ark Plugin 優(yōu)先 Ark Biz 被加載啟動(dòng);Ark Plugin 之間是雙向類索引關(guān)系,即可以相互委托對(duì)方加載所需的類和資源;Ark Plugin 和 Ark Biz 是單向類索引關(guān)系,即只允許 Ark Biz 索引 Ark Plugin 加載的類和資源,反之則不允許。
感覺(jué)我寫(xiě)文檔的能力有待提升,下面的圖 看得懂的人不需要看,看不懂的感覺(jué)看了也不懂......
源代碼包:
com.alipay.sofa.ark.api.ArkClient
com.alipay.sofa.ark.event.ArkEvent
com.alipay.sofa.spi.service.event.EventAdminService
主要在以上幾個(gè)類中
安裝操作 installOperation

卸載操作 uninstallOperation
卸載操作比較簡(jiǎn)單
發(fā)送事件通知
eventAdminService.sendEvent(new BeforeBizStopEvent(this));從concurrentHashMap緩存中刪除對(duì)應(yīng)版本
bizCache.remove(bizVersion);環(huán)繞事件通知
eventAdminService.sendEvent(new BeforeBizRecycleEvent(this));刪除工作目錄
bizTempWorkDir.delete();清除classloader 緩存
((AbstractClasspathClassLoader) classLoader).clearCache();停止后發(fā)送事件通知
eventAdminService.sendEvent(new AfterBizStopEvent(this));檢查操作 checkOperation
查看當(dāng)前所有版本的biz,其實(shí)就是從緩存中把biz及其所有版本獲取到
選擇操作 switchOperation
從cache中獲取當(dāng)前biz不同版本的集合,然后根據(jù)版本獲取當(dāng)前biz
ConcurrentHashMap<String, Biz> bizCache = bizRegistration.get(bizName);
if (bizCache != null) {
? ?return bizCache.get(bizVersion);
}
如果biz狀態(tài)不是active
eventAdminService.sendEvent(new BeforeBizSwitchEvent(biz));
bizManagerService.activeBiz(bizName, bizVersion);
eventAdminService.sendEvent(new AfterBizSwitchEvent(biz));
核心bizManagerService.activeBiz
其實(shí)就是將state設(shè)置為active,這樣就可以switch對(duì)應(yīng)的biz了
