淺析Java類隔離規(guī)避依賴沖突的實(shí)現(xiàn)原理
一、導(dǎo)語
隨著業(yè)務(wù)規(guī)模增長、業(yè)務(wù)邏輯演進(jìn),項(xiàng)目工程的依賴樹(二方依賴、三方依賴)變得愈發(fā)復(fù)雜。隨之而來的便是【依賴沖突】問題。
當(dāng)幾個(gè)軟件包對(duì)相同的共享包或庫有依賴性,但它們依賴于不同的、不兼容的共享包版本時(shí),就會(huì)出現(xiàn)依賴性問題。如果共享包或庫只能安裝一個(gè)版本,用戶可能需要通過獲得較新或較舊版本的依賴包來解決這個(gè)問題。反過來,這可能會(huì)破壞其他的依賴關(guān)系。
【依賴沖突】問題是軟件工程廣泛存在的問題,換句話說,各語言生態(tài)如Python、Golang、Nodejs、Java等都存在類似問題。但是由于Java語言的特殊機(jī)制,【依賴沖突】問題在Java中似乎有完美的解決方案,那就是【類隔離容器】。
從2000年的開源規(guī)范OSGI,到阿里巴巴自研Pandora容器,再到螞蟻金服開源sofa-ark,業(yè)界在【類隔離容器】這個(gè)領(lǐng)域的實(shí)踐方興未艾。那到底什么是類隔離容器?怎么實(shí)現(xiàn)類隔離容器?為什么它聽起來很完美但是卻沒有成為主流實(shí)踐?
本文代碼均為示意的偽代碼。
二、類隔離容器
當(dāng)項(xiàng)目依賴樹變得復(fù)雜時(shí),不可避免的會(huì)出現(xiàn)不同的組件依賴同一個(gè)組件的不同版本的問題。如下圖,3個(gè)組件分別依賴了 maven-settings 組件的2個(gè)版本:3.0、3.3.9;plexus-interpolation組件同理。
圖片
圖片
當(dāng)項(xiàng)目中只有一個(gè)依賴空間時(shí),項(xiàng)目需求的多個(gè)版本的組件最終只會(huì)有一個(gè)版本進(jìn)入項(xiàng)目依賴空間,極易因?yàn)樯蠈咏M件對(duì)版本需求的眾口難調(diào)而出現(xiàn)ClassNotFoundException、NoSuchMethodException等版本兼容性問題。
為解決這個(gè)問題,業(yè)界開始考慮通過Java類加載隔離來在項(xiàng)目運(yùn)行時(shí)創(chuàng)建多個(gè)隔離的依賴空間。每個(gè)依賴空間中可以各自使用相同組件的不同版本,這種隔離的依賴空間即為:類隔離容器。
如下圖,項(xiàng)目中存在3個(gè)類隔離容器,maven-settings組件在兩個(gè)容器中分別存在3個(gè)版本。
圖片
這里的maven-*只是Jar包名稱,和mvn工具無關(guān),只是筆者手上恰好有這個(gè)案例。
類隔離容器劫持、干預(yù)了Java類加載流程,讓同一個(gè)組件的多個(gè)版本可以在同一個(gè)項(xiàng)目中并存。
三、類加載API
Java是一種強(qiáng)類型的動(dòng)態(tài)語言,其代碼符號(hào)(類名、方法名、字段名)都在運(yùn)行時(shí)動(dòng)態(tài)鏈接,通過【類加載器】來實(shí)現(xiàn)運(yùn)行時(shí)的類搜索和代碼裝載。這種動(dòng)態(tài)特性賦予了框架開發(fā)者極大的便利性,支撐了大量企業(yè)級(jí)開發(fā)框架的實(shí)現(xiàn),提高了上層業(yè)務(wù)代碼的迭代效率。這也是Java語言二十幾年如一日占據(jù)編程語言排行榜前列的一個(gè)重要原因。
圖片
TIOBE編程社區(qū)指數(shù)-2024(https://www.tiobe.com/tiobe-index/)
為支撐上述類加載能力,同時(shí)賦予開發(fā)者自定義類加載流程的能力,Java Runtime定義了ClassLoader這一API。抽象的API如下:
圖片
ClassLoader的實(shí)現(xiàn)者負(fù)責(zé)根據(jù)【位置無關(guān)】的類標(biāo)識(shí),定位、裝載類。所謂【位置無關(guān)】說的是,JVM不關(guān)心這個(gè)類文件的物理位置是在網(wǎng)絡(luò)上、磁盤里、內(nèi)存里。
由于Classs類型的返回值無法由開發(fā)者自行構(gòu)造,涉及JVM內(nèi)部的狀態(tài)聯(lián)動(dòng),因此JVM會(huì)暴露一個(gè)構(gòu)造Class對(duì)象的工具API。抽象的API如下:
圖片
該parseAndLinkClass方法由JVM實(shí)現(xiàn),JVM內(nèi)部會(huì)進(jìn)行我們八股文都背過的類驗(yàn)證、類解析、類初始化等標(biāo)準(zhǔn)動(dòng)作。
因此,開發(fā)者自定義類加載流程的樣板代碼如下:
圖片
Java提供了類似上述樣板代碼的具體實(shí)現(xiàn),即:java.lang.ClassLoader,其實(shí)就是大家都熟悉的【模板方法設(shè)計(jì)模式】
上述通俗的、抽象的API能力,映射到Java的具體實(shí)現(xiàn)分別為:
裝載類
圖片
java.lang.ClassLoader#loadClass(java.lang.String)
定義類

java.lang.ClassLoader#defineClass0
JNI方法實(shí)現(xiàn)
圖片
jdk/src/share/native/java/lang/ClassLoader.c
四、類的相等性:
ClassCastException
盡管在源代碼層面,我們用【類的全限定名】作為編碼時(shí)定位類的標(biāo)識(shí),但是在JVM內(nèi)部,類的標(biāo)識(shí)是一個(gè)聯(lián)合索引。
JVM內(nèi)部使用 <ClassLoader,className> 二元組來索引、標(biāo)識(shí)一個(gè)類。通俗來說就是,兩個(gè)不同的 ClassLoader使用相同的類名和字節(jié)碼 defineClass得到的是兩個(gè)不同的Class對(duì)象。
通俗的偽代碼來表達(dá)的話,上述ClassLoadUtil#parseAndLinkClass方法的實(shí)現(xiàn)如下:
圖片
defineClass時(shí),創(chuàng)建的Class對(duì)象上會(huì)關(guān)聯(lián)Loader。
具體到Java中的java.lang.Class類,我們可以看到如下字段:
圖片
java.lang.Class#classLoader
上述類加載特性,在復(fù)雜的類加載邏輯下如果沒有處理好的話極易產(chǎn)生類型轉(zhuǎn)換異常:ClassCastException。如下示例:
圖片
圖片
圖片
如果Type類同時(shí)被兩個(gè)類加載器加載在JVM內(nèi)部產(chǎn)生了Type_1、Type_2兩個(gè)版本的類型(Class對(duì)象)。
LoadTest類中的Type符號(hào)鏈接到了Type_1。
TypeUtil類中的Type符號(hào)鏈接到了Type_2。
那么,當(dāng)LoadTest.main方法執(zhí)行時(shí)即會(huì)產(chǎn)生ClassCastException異常。
因?yàn)門ypeUtil.newType方法返回的Type_2類型的對(duì)象,和LoadTest.mian方法中聲明的Type_1類型的typeVar變量的類型不兼容,無法進(jìn)行隱式的類型轉(zhuǎn)換。
類隔離容器的需求天然需要同名類存在多個(gè)版本,因此類隔離容器的實(shí)現(xiàn)和使用時(shí)需要極小心的設(shè)計(jì)、處理該問題。這種問題排查起來非常費(fèi)勁。
五、類加載編排、委托
綜上分析,我們發(fā)現(xiàn)在Java層面實(shí)現(xiàn)load一個(gè)類并不復(fù)雜,只需要根據(jù)類名拿到二進(jìn)制的字節(jié)碼,然后調(diào)用JVM提供的工具方法就行了。
到這里,事情已經(jīng)回到我們最熟悉不過的CRUD主場(chǎng),我們可以用各種我們熟悉的設(shè)計(jì)模式來實(shí)現(xiàn)特定的類加載業(yè)務(wù)需求,其中最重要的設(shè)計(jì)模式即為:委托模式。
第一個(gè)業(yè)務(wù)需求是類加載的安全性。Java標(biāo)準(zhǔn)庫自帶了大量易用的工具和數(shù)據(jù)結(jié)構(gòu),這部分代碼的物理位置和業(yè)務(wù)代碼不在一起。為避免項(xiàng)目中的惡意代碼使用標(biāo)準(zhǔn)庫同名的類來干壞事,我們需要實(shí)現(xiàn)類加載優(yōu)先級(jí),即加載一個(gè)類時(shí)優(yōu)先從JRE目錄加載,JRE目錄中加載不到時(shí)再從項(xiàng)目中加載。
第二個(gè)業(yè)務(wù)需求是類的復(fù)用。如Tomcat場(chǎng)景,一個(gè)Tomcat進(jìn)程可以托管多個(gè)Web服務(wù)(war包)。每個(gè)Web服務(wù)自身的業(yè)務(wù)代碼和依賴是不同的,但是各個(gè)Web服務(wù)依賴的Servlet API、Tomcat API是相同的,因?yàn)檫@是Tomcat容器提供的公共的Runtime??紤]到上述【類的相等性】,我們希望這些Runtime類只有一個(gè)版本,以避免訪問Runtime API時(shí)出現(xiàn)ClassCastException。
那么我們重新實(shí)現(xiàn)上述AbstractClassLoader如下:
圖片
繼而我們可以基于上述模板類,構(gòu)造、編排我們的自定義類加載邏輯:
圖片
上述代碼通過編排類加載器,實(shí)現(xiàn)了如下項(xiàng)目依賴空間拓?fù)洌?/p>
圖片
綜上,我們?cè)贘ava Runtime的基礎(chǔ)ClassLoader機(jī)制上,通過非常熟悉的業(yè)務(wù)編排實(shí)現(xiàn)了類加載的安全性需求、共享復(fù)用需求,最終呈現(xiàn)了一個(gè)樹形的類加載器拓?fù)洹?/p>
不同的類加載需求需要編排出不同的類加載器拓?fù)?,比如我們討論的【類隔離容器】需求,需要編排出更復(fù)雜的類加載器拓?fù)?。但是其核心的編排思路都是相似的~
六、類加載劫持
到這里,我們已經(jīng)有足夠的技術(shù)儲(chǔ)備來根據(jù)業(yè)務(wù)需求編排類加載器拓?fù)湟赃_(dá)成目的。但是遺留了一個(gè)關(guān)鍵的問題:
怎么樣才能讓Java Runtime在加載、鏈接代碼符號(hào)時(shí),使用我們構(gòu)造出來的自定義類加載器呢?
因?yàn)槿绻覀儤?gòu)造出來的類加載器不能參與到類加載流程,那其實(shí)就是一個(gè)普通的Java對(duì)象,沒啥用。
要解決這個(gè)問題,我們需要參考JVM規(guī)范明確類加載器會(huì)被如何獲取和使用,因?yàn)轭惣虞d器本質(zhì)上是供Java Runtime使用的SPI。
JVM規(guī)范對(duì)這一塊的闡述是嚴(yán)謹(jǐn)?shù)橄蟮?,但通俗來說就一個(gè)原則:如果一個(gè)類C1是由CL加載器加載(defineClass)的,那么,C1觸發(fā)的的其他類如Cn的加載和鏈接,也會(huì)委托給CL。示例如下:
圖片
圖片
因?yàn)閍pp1.Main類是由app1Loader加載,那么app1.Main依賴的App1Service類也會(huì)隱式的交給app1Loader加載。這個(gè)過程是JVM在解析、鏈接app1.Main類的時(shí)候自動(dòng)進(jìn)行的。
也就是說,當(dāng)我們指定某個(gè)類加載器CL加載項(xiàng)目的EntryPoint并執(zhí)行后,后續(xù)觸發(fā)的類加載動(dòng)作都會(huì)交給指定的類加載器CL或者CL委托的其他類加載器。Java項(xiàng)目中的EntryPoint往往是項(xiàng)目中的main方法。
這里有點(diǎn)繞。換句話說,某個(gè)Class類對(duì)象C1依賴的其他類的加載都會(huì)交給C1.classLoader來進(jìn)行。注意,上面【類的相似性】一節(jié)說過,每個(gè)Class對(duì)象上都持有加載它的ClassLoader的引用。
那么,想讓3個(gè)WebApp在各自類空間中運(yùn)行的方式就很簡(jiǎn)單了:
圖片
上述流程還遺留一個(gè)問題,那就是ServiceLoader場(chǎng)景。
為打破呆板的雙親委派機(jī)制實(shí)現(xiàn)某種意義上的IOC,Java提供了contextClassLoader機(jī)制。contextClassLoader關(guān)聯(lián)在Thread對(duì)象上,并且會(huì)在父子線程中復(fù)制、傳播。
java.lang.Thread#getContextClassLoader
為了讓上述3個(gè)WebApp中正常使用ServiceLoaderAPI或類似的SPI框架,我們需要做如下特別處理:
至此,我們就實(shí)現(xiàn)了Tomcat場(chǎng)景下的類加載劫持、類隔離、類共享。
到這里,我們已經(jīng)掌握了實(shí)現(xiàn)類隔離容器的核心基礎(chǔ)。
總結(jié)來說,只要我們能在應(yīng)用的EntryPoint(main方法)中合理的介入、干預(yù),就能實(shí)現(xiàn)靈活的類加載業(yè)務(wù)。
七、類隔離模塊:Bundle
回到最開始的需求,我們希望可以在項(xiàng)目中達(dá)成如下依賴結(jié)構(gòu):

為了實(shí)現(xiàn)版本隔離、共存,上述mave-core、maven-compat、maven-xxx組件會(huì)將其依賴的maven-settings Jar文件按特定布局打包到自身的jar包中,形成各自獨(dú)立的依賴空間,供運(yùn)行時(shí)提取、加載。
OSGI中將上述隔離的依賴空間或者類隔離容器稱為bundle。需要進(jìn)行類隔離的組件按bundle文件布局來交付自己的代碼和依賴。
圖片
每個(gè)bundle是一個(gè)FatJar,通俗來說是一個(gè)包含自身依賴的Jar文件的Jar文件。類似如下Jar文件:
圖片
可以把上述dubbo-demo jar文件想象成我們熟悉的mybatis框架。該模塊把mybaits框架自身的代碼和它依賴的三方包按設(shè)計(jì)的布局打包到同一個(gè)Jar中。
我們知道,Java自帶的URLClassLoader天然支持從Jar文件中搜索、讀取class文件,但是不支持上述嵌套Jar。
解決這個(gè)問題有兩個(gè)方案:
解壓FatJar
在進(jìn)程啟動(dòng)時(shí),類隔離容器底座識(shí)別出ClassPatch中存在上述類型的bundle Jar后,提前將上述FatJar解壓到本地磁盤。后續(xù)就簡(jiǎn)單了,無非就是在指定目錄搜索類和Jar。
文件切片
我們知道,Jar文件本質(zhì)上就是ZIP格式的文件,而ZIP文件的邏輯結(jié)構(gòu)是一個(gè)Map。
圖片
如上圖,test.jar中有兩個(gè)文件,一個(gè)是a.b.C.class文件,另一個(gè)是dep.jar。該文件在磁盤上的抽象布局如下:
圖片
ZIP文件除文件元數(shù)據(jù)外,整體分為兩部分。
- 數(shù)據(jù)區(qū):存放文件的內(nèi)容。
 - 索引區(qū):存放文件名稱和文件內(nèi)容的偏移量和長度。
 
因此,在技術(shù)上我們可以對(duì)FatJar文件做切片。即在不解壓FatJar的前提下,將其中一個(gè)區(qū)間當(dāng)成jar文件讀取。如上圖,我們解析test.jar索引區(qū)得到dep.jar文件的長度為4000字節(jié),在外層jar文件的1000偏移處,那么我們讀取它內(nèi)部嵌套的Jar文件的偽代碼如下:
圖片
這一塊說來話長,全是花活。spring-boot就是使用類似方式來拍平嵌套的Jar文件。
可以參考相關(guān)資料:
【SpringBoot】服務(wù) Jar 包的啟動(dòng)過程原理(https://www.cnblogs.com/kukuxjx/p/18207068)
八、類導(dǎo)入/導(dǎo)出:Bundle元信息
到這里,我們可以初步勾勒類隔離容器的代碼藍(lán)圖了。
圖片
- 【底座】需要在執(zhí)行流進(jìn)入業(yè)務(wù)main方法前,提前執(zhí)行。
 - 【底座】掃描項(xiàng)目中的依賴,區(qū)分Jar依賴和Bundle依賴。
 - 【底座】為每個(gè)Bundle依賴創(chuàng)建獨(dú)立的Bundle類加載器(N個(gè))。
 - 【底座】為Bundle以外的業(yè)務(wù)代碼和普通Jar創(chuàng)建類加載器(1個(gè))。
 - 【底座】將上述N+1個(gè)類加載器狀態(tài)編排到一起,拼湊成完整的依賴視圖。
 - 【底座】初始化當(dāng)前線程contextClassLoader。
 - 【底座】使用業(yè)務(wù)類加載器搜索、加載main方法所在類(EntryPoint)。
 - 【底座】調(diào)用業(yè)務(wù)代碼的main方法。
 
這里存在兩個(gè)問題:
- 【底座】怎么區(qū)分ClassPath下的Jar文件是普通Jar還是Bundle Jar?這個(gè)一般是通過在打包時(shí)向Bundle Jar中注入特征文件來實(shí)現(xiàn)。比如sofa-ark在打包Bundle Jar時(shí),會(huì)在Jar中注入如下路徑固定的標(biāo)記文件:com/alipay/sofa/ark/plugin/mark
 
圖片
com.alipay.sofa.ark.spi.constant.Constants#ARK_PLUGIN_MARK_ENTRY
圖片
- 項(xiàng)目中有那么多類加載器(N+1),當(dāng)我們加載一個(gè)類時(shí),到底應(yīng)該由哪個(gè)類加載加載呢?這部分信息是控制bundle正常工作的元信息,需要每個(gè)bundle的維護(hù)者提供給【底座】讀取、使用。即,每個(gè)bundle中必須要提供這個(gè)Bundle導(dǎo)出的類、導(dǎo)入的類。這類元信息一般會(huì)在bundle jar的Manifest文件提供,下圖是一個(gè)OSGI規(guī)范下,bundle jar中Manifest文件提供的類導(dǎo)入/導(dǎo)出信息。
 
圖片
上述信息表明:
- 該bundle jar向外暴露com.sample.myservice.api,即該包下的類由這個(gè)bundle類加載來加載。
 - 該bundle jar依賴了org.apache.commons.logging,需要由其他類加載器來加載、提供。
 
是不是有點(diǎn)像 JDK9 的新特性:模塊化?
除此之外,一般還會(huì)提供優(yōu)先級(jí)等其他用于控制類加載過程的元信息,畢竟可能有多個(gè)bundle暴露相同的類。相關(guān)的細(xì)節(jié)信息很多,在各個(gè)具體的實(shí)現(xiàn)(開源的OSGI、阿里巴巴的Pandora、螞蟻金服的sofa-ark)上可能有差異,但是大同小異。
九、Bundle依賴隔離
到這里,我們終于可以看下怎么實(shí)現(xiàn)一個(gè)類隔離容器了。
業(yè)務(wù)類加載器
圖片
Bundle類加載器
圖片
類加載器管理器
圖片
類隔離容器底座
圖片
圖片
使用姿勢(shì)
圖片
上述代碼僅為理論示意,并不能直接運(yùn)行,讀者會(huì)意即可。
以上,我們就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的類隔離容器,最終形成的類加載器拓?fù)淙缦拢?/p>
圖片
最終實(shí)現(xiàn)了每個(gè)Bundle優(yōu)先使用自身內(nèi)部嵌入的Jar依賴,從而實(shí)現(xiàn)每個(gè)Bundle Jar有一個(gè)獨(dú)立的依賴空間,避免了依賴沖突。
十、沒有銀彈
當(dāng)bundle jar中的嵌套依賴不向外逃逸時(shí),一切都工作的很好。但是如果嵌套依賴中的API被跨bundle耦合、交互,那事情就變得棘手起來。
考慮如下的場(chǎng)景:
圖片
- Bundle BBB中導(dǎo)出了如下Service:
 
圖片
- Bundle AAA中導(dǎo)出了如下Service:
 
圖片
- AAAService依賴了lang3-1.0中的Pair類,BBBService依賴了lang3-2.0中的Pair類。那么請(qǐng)問,AAAService這個(gè)類,應(yīng)該使用哪個(gè)版本的lang3?1.0還是2.0?
 
使用1.0版本:action2方法可以正常工作,因?yàn)閍ction2方法就是在1.0版本下編寫、編譯的。但是這個(gè)這樣action1方法又無法正常工作了。因?yàn)閍ction1方法中調(diào)用的BBBService的action方法預(yù)期的參數(shù)類型是2.0版本的Pair類。
使用2.0版本:那還是上面同樣的道理。
如果AAAService使用1.0版本的Pair類,BBBService使用2.0版本的Pair類,那么又會(huì)出現(xiàn)我們上面著重強(qiáng)調(diào)過的【類的相等性】問題,一定會(huì)產(chǎn)生ClassCastException。
十一
總結(jié)
剛進(jìn)入一個(gè)陌生領(lǐng)域就陷入代碼細(xì)節(jié)并不是一個(gè)高效的方式,所以本文中筆者盡可能的使用偽代碼、示意代碼來進(jìn)行論述。
一路梳理下來,我們最終通過類加載器編排,實(shí)現(xiàn)了一個(gè)理論上的類隔離容器。盡管沒有具體的代碼實(shí)現(xiàn),但是相信看到這里,讀者們已經(jīng)對(duì)類隔離機(jī)制有了一個(gè)較為系統(tǒng)的認(rèn)識(shí)。
總的來說,Java類隔離容器的思路是在Java語言既有特性的基礎(chǔ)上,利用類加載劫持、類加載器編排實(shí)現(xiàn)了一套多版本類并存的機(jī)制,確實(shí)可以減少某些場(chǎng)景下的類版本沖突的問題。但是它解決了一些問題,但是同樣的也帶來了新的問題。
- 排障心智:類隔離機(jī)制構(gòu)造了一個(gè)復(fù)雜的類加載器拓?fù)洌?dāng)因?yàn)閏ornor case出現(xiàn)了類加載異常時(shí),bundle組件的使用者是一臉懵逼的。本來遇到類似ClassNotFoundException、NoSuchMethodException問題時(shí),組件使用者可以根據(jù)項(xiàng)目依賴樹所見即所得的按沉淀的經(jīng)驗(yàn)排查、處置。但是當(dāng)你用【嵌套Jar+編排加載機(jī)制】交付組件后,之前沉淀的相關(guān)排障心智都沒用了。
 - 遷移成本:在組織從0到1起步階段介入進(jìn)行上述改造是合適的、成本極低的,但是沒人能顧得上這個(gè)。在組織從80到100的階段發(fā)現(xiàn)類隔離機(jī)制能解決一些問題,但是這個(gè)時(shí)期各個(gè)業(yè)務(wù)項(xiàng)目的代碼結(jié)構(gòu)、組件版本、組件使用姿勢(shì)百花齊放。想要技改、收斂到bundle jar模式,成本比較大且客觀上存在一個(gè)研發(fā)效率、業(yè)務(wù)穩(wěn)定性的陣痛期。
 - 元信息維護(hù):如上梳理,bundle jar交付時(shí),bundle維護(hù)者需要梳理其導(dǎo)出的類、導(dǎo)入的類。這個(gè)只能人肉梳理,可能會(huì)漏、可能會(huì)錯(cuò);且因?yàn)槎鄠€(gè)bundle在運(yùn)行期的化學(xué)反應(yīng),漏、錯(cuò)的異常表現(xiàn)很不直觀,難以診斷、排查。如果各個(gè)bundle維護(hù)者都在一個(gè)部門下那溝通、處理起來還好,如果是跨部門的多個(gè)bundle互相打架,事情就比較麻煩。
 
筆者以為,類隔離機(jī)制的高價(jià)值場(chǎng)景應(yīng)該是特定領(lǐng)域內(nèi)部使用的JVM租戶。由于JVM比較吃資源,某些輕量邏輯(FAAS)如果單獨(dú)啟動(dòng)一個(gè)進(jìn)程來執(zhí)行,有點(diǎn)類似于用集裝箱運(yùn)一只籃球,性價(jià)比很低,那干脆大家一起眾籌拼集裝箱得了。

又回到了十年前用Tomcat托管多個(gè)WebApp的模式...
如上圖,在JVM進(jìn)程上構(gòu)建一個(gè)應(yīng)用引擎,可以根據(jù)JVM資源情況動(dòng)態(tài)的將包含代碼和依賴的bundle jar調(diào)度到JVM上運(yùn)行。JVM租戶的主要問題是資源隔離性不夠,比如CPU、MEM和IO。但是如果這個(gè)平臺(tái)只是內(nèi)部特定場(chǎng)景下、特定開發(fā)人員使用問題倒也不大。
以上均為筆者一家之言,歡迎指正~
參考
- 微服務(wù)的災(zāi)難-依賴地獄(https://xargin.com/disaster-of-microservice-dephell/)
 
- 如何打包 Ark Plugin(https://www.sofastack.tech/projects/sofa-boot/sofa-ark-ark-plugin-demo/)
 
- OSGi 捆綁軟件清單文件(https://www.ibm.com/docs/zh/was-zos/9.0.5?topic=files-example-osgi-bundle-manifest-file)
 















 
 
 






 
 
 
 