邵建偉,劉其群,王煥強(qiáng),陳耀旺,俞東進(jìn)*,SALAMAT Boranbaev
(1.浙江天正思維信息技術(shù)有限公司,杭州 310006;2.杭州電子科技大學(xué)計(jì)算機(jī)學(xué)院,杭州 310018;3.河南農(nóng)業(yè)職業(yè)學(xué)院旅游管理學(xué)院,鄭州 451450)
(?通信作者電子郵箱yudj@hdu.edu.cn)
傳統(tǒng)的軟件系統(tǒng)往往基于單體式架構(gòu)開發(fā),所有的組件、模塊和資源等都集中在同一個(gè)軟件實(shí)體中。然而,隨著業(yè)務(wù)復(fù)雜度的不斷攀升,單體式軟件系統(tǒng)的整體復(fù)雜度和系統(tǒng)各個(gè)組件之間的耦合度變得越來越高,代碼也因此變得更加不可控且難以理解。微服務(wù)架構(gòu)作為一個(gè)靈活的軟件架構(gòu)體系,采用了分布式的服務(wù)管理模式,它將一個(gè)龐大的復(fù)雜軟件系統(tǒng)分解成一組相互配合的小型服務(wù),即微服務(wù),從而能很好地解決單體式架構(gòu)高復(fù)雜度和高耦合度等問題。但是將一個(gè)單體式遺留軟件系統(tǒng)遷移至微服務(wù)架構(gòu)是一項(xiàng)代價(jià)較高的任務(wù),開發(fā)人員不僅要理清業(yè)務(wù)邏輯,更要理解程序的運(yùn)行方式。特別是,如果系統(tǒng)功能高度耦合,這項(xiàng)工作將會(huì)變得極具挑戰(zhàn)性。因此,設(shè)計(jì)一種可自動(dòng)化識(shí)別微服務(wù)的方法對于將遺留軟件系統(tǒng)遷移至微服務(wù)架構(gòu)至關(guān)重要。
眾所周知,類通常被用來描述某個(gè)單一實(shí)體資源的屬性和行為,存在依賴關(guān)系的兩個(gè)類所操作的資源數(shù)據(jù)之間一般存在著一定相關(guān)性。而根據(jù)微服務(wù)定義,一個(gè)業(yè)務(wù)微服務(wù)可以理解為對一個(gè)業(yè)務(wù)資源的緊密相關(guān)的操作的集合?;谏鲜鏊悸罚疚奶岢隽艘环N資源約束下基于類依賴關(guān)系的微服務(wù)識(shí)別方法。這里所謂的資源約束是指耦合較為緊密的不同類所操作的資源數(shù)據(jù)往往存在一定相關(guān)性這一事實(shí)。該方法根據(jù)遺留軟件程序中的類依賴關(guān)系構(gòu)建類依賴關(guān)系圖,并設(shè)計(jì)了基于資源實(shí)體標(biāo)簽的類依賴關(guān)系圖劃分算法,通過合并依賴程度較高的候選微服務(wù),從而得到最終的微服務(wù)集合。實(shí)驗(yàn)結(jié)果表明,所提方法具有較高的微服務(wù)劃分準(zhǔn)確率,驗(yàn)證了同時(shí)考慮不同類之間的依賴關(guān)系和資源約束對于微服務(wù)識(shí)別的合理性和有效性。
在遷移微服務(wù)的過程中,一個(gè)重要的挑戰(zhàn)是如何從遺留軟件系統(tǒng)中識(shí)別并提取微服務(wù)。在工業(yè)界,微服務(wù)的識(shí)別與提取大多是由經(jīng)驗(yàn)豐富的系統(tǒng)架構(gòu)師和業(yè)務(wù)專家一同從業(yè)務(wù)的角度著手,運(yùn)用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)[1]的方法對系統(tǒng)的業(yè)務(wù)領(lǐng)域進(jìn)行領(lǐng)域建模,逐步分解、識(shí)別并提取微服務(wù)[2-3]。在實(shí)際應(yīng)用中,Dragoni 等[4]率先嘗試將丹麥銀行的外匯核心系統(tǒng)FX Core 遷移至微服務(wù)架構(gòu)。Gouigoux 等[5]幫助法國軟件供應(yīng)商MGDIS SA基于微服務(wù)架構(gòu)重新實(shí)現(xiàn)他們的現(xiàn)有應(yīng)用程序,現(xiàn)已完成且面向公眾開放運(yùn)行。同時(shí),他們提出了將遺留軟件系統(tǒng)遷移到微服務(wù)架構(gòu)時(shí)的三個(gè)關(guān)鍵點(diǎn):合適的微服務(wù)粒度、合適的部署方式和有效的編排方式。Lotz 等[6]基于微服務(wù)架構(gòu)對高級(jí)駕駛員輔助系統(tǒng)(Advanced Driver Assistant System,ADAS)項(xiàng)目進(jìn)行案例研究,討論了如何解決微服務(wù)架構(gòu)中錯(cuò)誤分配服務(wù)等潛在危害,并分析了微服務(wù)架構(gòu)在汽車場景中的弱點(diǎn)和威脅。O’Brien 等[7]構(gòu)建了基于微服務(wù)架構(gòu)提供健康解決方案的服務(wù)平臺(tái):BPM(Beats-Per-Minute)。該平臺(tái)展示了微服務(wù)架構(gòu)如何有助于BPM 系統(tǒng)持續(xù)獲取、分析和可視化健康相關(guān)數(shù)據(jù)。王煥強(qiáng)等[8]引入第三方非對稱混合加密方法,將其封裝為微服務(wù),以解決基于微服務(wù)架構(gòu)的業(yè)務(wù)流程管理平臺(tái)的通信安全問題。
在學(xué)術(shù)界,研究人員也開展了大量的研究工作。Rahman等[9]描述了行為驅(qū)動(dòng)開發(fā)(Behavior-Driven Development,BDD)在微服務(wù)架構(gòu)中的應(yīng)用,以減少開發(fā)人員的維護(hù)負(fù)擔(dān)并鼓勵(lì)使用驗(yàn)收測試。Pahl等[10]對現(xiàn)有的微服務(wù)研究機(jī)構(gòu)進(jìn)行了審查和分類,發(fā)現(xiàn)在現(xiàn)有技術(shù)水平上缺乏對微服務(wù)的工具支持。Gysel 等[11]提出了Service Cutter 方法以試圖提供一種從原始單體式架構(gòu)中識(shí)別微服務(wù)的結(jié)構(gòu)化方法,提出了一種通過圖切割的方法來支持結(jié)構(gòu)化服務(wù)分解的工具,把軟件描述和文檔(比如領(lǐng)域模型和用例)作為輸入輸出以構(gòu)建圖中各節(jié)點(diǎn)的耦合值;但是,該方法無法自動(dòng)從遺留軟件系統(tǒng)挖掘或構(gòu)造必要的結(jié)構(gòu)信息,因此必須依賴用戶在特定的模型中提供軟件信息。Levcovitz 等[12]提出可以從用戶界面及其調(diào)用的業(yè)務(wù)功能和此功能所使用的數(shù)據(jù)庫表著手,通過對這些元素進(jìn)行分組,以此識(shí)別微服務(wù);然而,該方法的主要不足是它基于關(guān)于待分解的整體結(jié)構(gòu)(即MVC(Model View Controller)架構(gòu))的限制性假設(shè)。Mazlami等[13]提出了三種從單體式應(yīng)用程序中提取微服務(wù)的耦合策略(邏輯耦合策略、語義耦合策略和貢獻(xiàn)者耦合策略),并將其嵌入基于圖的聚類算法中,得到候選的微服務(wù);但是在該方法中,其耦合策略取決于代碼的版本提交歷史。所以,如果它不可用或僅由有限數(shù)量的代碼提交組成,則該方法難以達(dá)到預(yù)期的效果。Baresi 等[14]則提出了一種基于OpenAPI(Application Programming Interface)規(guī)范中接口信息識(shí)別微服務(wù)的方法,通過將規(guī)范中使用的API 術(shù)語作為輸入并與參考詞匯表進(jìn)行匹配來進(jìn)行系統(tǒng)分解,以識(shí)別潛在的候選微服務(wù);該方法的局限性在于依賴開發(fā)者必須提供有意義和良好定義的接口。Amiri 等[15]提出了一種基于業(yè)務(wù)流程來識(shí)別微服務(wù)的方法,該方法首先提取出業(yè)務(wù)流程,然后找出每個(gè)業(yè)務(wù)流程對實(shí)體的一個(gè)(讀/寫)操作情況以構(gòu)建業(yè)務(wù)之間的相關(guān)性,最后分類提取候選微服務(wù)。此外,微服務(wù)粒度的界定也是微服務(wù)領(lǐng)域的一項(xiàng)難題。為此,鐘陳星等[16]提出了4 項(xiàng)評(píng)估指標(biāo),用于衡量微服務(wù)劃分的合理性,并基于此進(jìn)一步提出了一種基于限界上下文的微服務(wù)粒度評(píng)估模型。
定義1類依賴關(guān)系。如兩個(gè)類存在方法調(diào)用、繼承和類型依賴等關(guān)系時(shí),則稱這兩個(gè)類具有類依賴關(guān)系,記為,其中,Ccaller表示依賴關(guān)系的發(fā)起者,Ccallee表示被依賴類。類依賴關(guān)系反映了類與類之間的耦合程度。
定義2類依賴關(guān)系圖。類依賴關(guān)系圖G=(V,E)表示了軟件程序中存在的類依賴關(guān)系,其中:V為圖中所有頂點(diǎn)的集合,定義為V={v1,v2,…,vi,…,vn},每個(gè)頂點(diǎn)vi對應(yīng)了軟件程序中的一個(gè)類ci;E為圖中所有有向邊的集合,記為E={e1,e2,…,ej,…,em},每條邊ej即一組類依賴關(guān)系ej=。圖1給出了一個(gè)類依賴關(guān)系圖的示例。
圖1 類依賴關(guān)系圖示例Fig.1 Example of class dependency graph
定義3實(shí)體類型,實(shí)體類型集合。實(shí)體類型集合是表示用戶輸入的資源類型的集合,記為R={r1,r2,…,rk,…,rl},其中rk即表示一個(gè)實(shí)體類型。
定義4實(shí)體匹配度。實(shí)體匹配度表示實(shí)體類型在類源碼中的重要程度,記為s(rk,cp)。
定義5實(shí)體標(biāo)簽。實(shí)體標(biāo)簽表示類依賴關(guān)系圖中節(jié)點(diǎn)vi所對應(yīng)的實(shí)體類型,記為tagrk。如圖1 所示,節(jié)點(diǎn)v1對應(yīng)的實(shí)體類型為A,實(shí)體標(biāo)簽記為tagA,節(jié)點(diǎn)v2對應(yīng)的實(shí)體類型為B,實(shí)體標(biāo)簽記為tagB。
定義6繼承的實(shí)體標(biāo)簽數(shù)組。由一個(gè)節(jié)點(diǎn)vi的所有直接父節(jié)點(diǎn)的實(shí)體標(biāo)簽及其繼承的實(shí)體標(biāo)簽集合組成的集合,記為PTags(vi)=[tag1,tag2,…,tagh]。若vi是起始節(jié)點(diǎn),則其繼承的實(shí)體標(biāo)簽集合為空,即PTags(vi)=[]。
在圖1 中,節(jié)點(diǎn)v1和節(jié)點(diǎn)v2的繼承的實(shí)體標(biāo)簽數(shù)組為[],節(jié)點(diǎn)v4的繼承的實(shí)體標(biāo)簽數(shù)組為[tagA,tagB]。
定義7類依賴關(guān)系標(biāo)記圖。將類依賴關(guān)系圖G中所有節(jié)點(diǎn)打上實(shí)體標(biāo)簽之后得到的新的圖模型稱之為類依賴關(guān)系標(biāo)記圖,記為Gtag。
定義8關(guān)鍵節(jié)點(diǎn)。在類依賴關(guān)系標(biāo)記圖中同時(shí)滿足以下兩個(gè)條件的節(jié)點(diǎn)稱為關(guān)鍵節(jié)點(diǎn)。條件1:該節(jié)點(diǎn)的繼承的實(shí)體標(biāo)簽數(shù)組中包含了多個(gè)不同類型的實(shí)體標(biāo)簽;條件2:該節(jié)點(diǎn)的不同父節(jié)點(diǎn)中的實(shí)體標(biāo)簽及其繼承的實(shí)體標(biāo)簽數(shù)組中的實(shí)體標(biāo)簽的類型不同。
如圖1 所示,節(jié)點(diǎn)v4就是一個(gè)關(guān)鍵節(jié)點(diǎn),因?yàn)樵摴?jié)點(diǎn)的繼承的實(shí)體標(biāo)簽數(shù)組中包含了tagA和tagB這兩個(gè)不同類型的實(shí)體標(biāo)簽,且其父節(jié)點(diǎn)v1、v2包含的實(shí)體標(biāo)簽也不同,分別為tagA和tagB。而節(jié)點(diǎn)v8則不是一個(gè)關(guān)鍵節(jié)點(diǎn),因?yàn)樵摴?jié)點(diǎn)繼承的實(shí)體標(biāo)簽數(shù)組中僅包含了tagB這一類實(shí)體標(biāo)簽。因?yàn)槲⒎?wù)識(shí)別的關(guān)鍵是將原有軟件劃分為一系列耦合度較少的服務(wù)模塊,從資源調(diào)用角度講,即需要滿足:單個(gè)服務(wù)模塊內(nèi)部的相同實(shí)體類型盡可能多、不同服務(wù)模塊共享的相同實(shí)體類型盡可能少,因此找到與不同實(shí)體類型相關(guān)的關(guān)鍵節(jié)點(diǎn)至關(guān)重要。
本文方法的整體流程分為以下4個(gè)步驟,如圖2所示。
圖2 資源約束下的基于類依賴關(guān)系的微服務(wù)識(shí)別方法整體流程Fig.2 Overall process of microservice identification method based on class dependencies under resource constraints
1)構(gòu)建類依賴關(guān)系圖。從軟件源碼中提取類的依賴關(guān)系轉(zhuǎn)換成Caller?Callee二維數(shù)組,并構(gòu)建類依賴關(guān)系圖模型。
2)設(shè)置實(shí)體標(biāo)簽。對軟件源碼進(jìn)行詞法分析并構(gòu)建類語料庫,通過實(shí)體類型集合設(shè)置每個(gè)類的實(shí)體標(biāo)簽,并繼承和重置自身的實(shí)體標(biāo)簽,得到類依賴關(guān)系標(biāo)記圖。
3)圖模型劃分。根據(jù)本文提出的基于實(shí)體標(biāo)簽的劃分算法對類依賴關(guān)系標(biāo)記圖進(jìn)行分割。
4)合并微服務(wù)。根據(jù)微服務(wù)對外依賴程度合并聯(lián)系緊密的候選微服務(wù)。
本文使用如下方法識(shí)別類依賴關(guān)系:首先,使用工具javacallgraph(https://github.com/gousiosg/java-callgraph)從軟件程序源碼中提取出類之間所有的依賴關(guān)系保存為Caller?Callee二維數(shù)組,其中Caller?Callee二維數(shù)組的第一維度是依賴類Ccaller,第二維度是被依賴類Ccallee。然后對Caller?Callee二維數(shù)組進(jìn)行依賴關(guān)系清洗,清洗操作包含以下3個(gè)步驟:
1)過濾無關(guān)依賴。開源工具java-callgraph 提取出來的依賴關(guān)系包含大量對第三方類的依賴,比如對java.util.*和org.springframework.*的依賴關(guān)系。這些依賴關(guān)系的存在對研究軟件資源的相關(guān)性毫無幫助,還會(huì)影響類依賴圖結(jié)構(gòu)的完整性。
2)過濾重復(fù)依賴。因?yàn)轭愐蕾噲D屬于有向無權(quán)圖,每一對依賴只需要記錄一次即可,所以需要把重復(fù)的依賴關(guān)系過濾掉。
3)過濾自我依賴。為了避免后續(xù)設(shè)置實(shí)體標(biāo)簽時(shí)的自我循環(huán)設(shè)置,需要預(yù)先過濾自我依賴的類依賴關(guān)系。
最后將清洗后的Caller?Callee二維數(shù)組構(gòu)建為類依賴關(guān)系圖G,并用鄰接矩陣表示,其中Ai,j表示節(jié)點(diǎn)vi和節(jié)點(diǎn)vj在圖中是否存在依賴關(guān)系,其計(jì)算方法如式(1)所示:
為了給每個(gè)類設(shè)置實(shí)體標(biāo)簽,需要提取出軟件程序中所有類的源碼,并根據(jù)用戶設(shè)置的實(shí)體類別,計(jì)算每個(gè)類實(shí)體匹配度,將值最高的實(shí)體類別作為該類的實(shí)體標(biāo)簽。
1)詞法分析。
軟件程序的類中包含大量體現(xiàn)類作用的代碼元素(類名、方法名和變量名等),但是也存在一些對理解類作用沒有幫助的元素(停用詞和關(guān)鍵字等),所以需要從軟件源程序中提取出所有能體現(xiàn)類作用的代碼元素。為此,本文將軟件程序中的每一個(gè)類分別構(gòu)建成一棵抽象語法樹(Abstract Syntax Tree,AST),再從對應(yīng)AST 中的類節(jié)點(diǎn)提出類源碼,并對獲得的類源碼進(jìn)行詞法分析,構(gòu)建一個(gè)由類組成的語料庫C={c1,c2,…,cp,…,cn},其中語料庫中的每個(gè)元素cp由源碼中對應(yīng)類的詞匯組成。
2)設(shè)置節(jié)點(diǎn)自身的實(shí)體標(biāo)簽。
實(shí)體匹配度表示實(shí)體類型在類源程序中的重要程度。本文方法使用詞頻-逆文檔頻率(Term Frequency-Inverse Document Frequency,TF-IDF)算法計(jì)算類與實(shí)體類型rk的實(shí)體匹配度s(rk,cp),計(jì)算公如式(2)所示:
此外,在軟件程序中,類可以分為負(fù)責(zé)業(yè)務(wù)邏輯的業(yè)務(wù)功能類和提供通用操作的工具類。前者一般具有較高的實(shí)體辨識(shí)度,而后者則相反。如式(3)所示,對于業(yè)務(wù)功能類,可以直接選出實(shí)體匹配度最高的那個(gè)實(shí)體類型作為該類的實(shí)體標(biāo)簽;而對于工具類,因?yàn)槠渑c任何實(shí)體類型計(jì)算所得實(shí)體匹配度都為0,所以將其實(shí)體標(biāo)簽設(shè)置為NULL。
最后在計(jì)算完類cp的實(shí)體標(biāo)簽tagrk之后,將其標(biāo)記到類依賴關(guān)系圖中對應(yīng)節(jié)點(diǎn)上,得到新的類依賴關(guān)系標(biāo)記圖Gtag。
3)繼承父節(jié)點(diǎn)的實(shí)體標(biāo)簽。
因?yàn)橐唤M存在依賴關(guān)系的兩個(gè)類具有資源操作相關(guān)性,所以為了更好地表示類之間的資源上的聯(lián)系,類依賴關(guān)系圖中的后續(xù)節(jié)點(diǎn)的繼承的實(shí)體標(biāo)簽數(shù)組應(yīng)該包含所有父節(jié)點(diǎn)的實(shí)體標(biāo)簽及其繼承的實(shí)體標(biāo)簽數(shù)組。如圖3 所示,節(jié)點(diǎn)v5本身的實(shí)體標(biāo)簽為tagA,且該類分別被節(jié)點(diǎn)v1、節(jié)點(diǎn)v2和節(jié)點(diǎn)v4所依賴,那么節(jié)點(diǎn)v5最終繼承實(shí)體標(biāo)簽數(shù)組PTags(v5)為[tagA,tagB,tagC,tagC]。
圖3 繼承父節(jié)點(diǎn)實(shí)體標(biāo)簽示意圖Fig.3 Schematic diagram of inheriting entity labels of parent nodes
4)重置實(shí)體標(biāo)簽。
對于任意節(jié)點(diǎn)vi,如果該節(jié)點(diǎn)的所有父節(jié)點(diǎn)均屬于同一實(shí)體類型,但是與該節(jié)點(diǎn)自身的實(shí)體標(biāo)簽不同,則將該節(jié)點(diǎn)自身的實(shí)體標(biāo)簽更新為父節(jié)點(diǎn)的實(shí)體標(biāo)簽。如圖4 所示,節(jié)點(diǎn)v4自身的實(shí)體標(biāo)簽為tagB,而其所有父節(jié)點(diǎn)v1、v2和v3的實(shí)體標(biāo)簽均為tagA,所以應(yīng)當(dāng)將節(jié)點(diǎn)v4的實(shí)體標(biāo)簽更新為父節(jié)點(diǎn)的實(shí)體標(biāo)簽tagA。
為了從軟件系統(tǒng)中識(shí)別出合理的微服務(wù),本文將軟件系統(tǒng)按照實(shí)體類型進(jìn)行劃分,即一個(gè)微服務(wù)中的類所標(biāo)記的實(shí)體類型盡可能一致。因此,本節(jié)提出了基于實(shí)體標(biāo)簽的圖模型劃分方法,該方法的劃分原則是將重置實(shí)體標(biāo)簽后的類依賴關(guān)系標(biāo)記圖Gtag劃分成一系列沒有交集的子圖,且滿足子圖內(nèi)部節(jié)點(diǎn)的相同實(shí)體類型盡可能多、子圖之間的相同實(shí)體類型盡可能少。其關(guān)鍵是要識(shí)別類依賴關(guān)系標(biāo)記圖中所有合理的關(guān)鍵節(jié)點(diǎn),并根據(jù)對應(yīng)的劃分規(guī)則將所有關(guān)鍵節(jié)點(diǎn)劃分至合理的子圖中,這些相互獨(dú)立的子圖就是一個(gè)個(gè)初步識(shí)別的候選微服務(wù)。
圖4 重置實(shí)體標(biāo)簽示意圖Fig.4 Schematic diagram of resetting entity labels
1)識(shí)別合理的關(guān)鍵節(jié)點(diǎn)。
如何識(shí)別類依賴關(guān)系標(biāo)記圖中的關(guān)鍵節(jié)點(diǎn)是本文方法的關(guān)鍵之一,對于任一節(jié)點(diǎn),若該節(jié)點(diǎn)是關(guān)鍵節(jié)點(diǎn),則必須要遞歸判斷父節(jié)點(diǎn)是否為關(guān)鍵節(jié)點(diǎn)。這是因?yàn)楫?dāng)一個(gè)節(jié)點(diǎn)vA的父節(jié)點(diǎn)和祖先節(jié)點(diǎn)中也存在關(guān)鍵節(jié)點(diǎn)vp時(shí),如果把節(jié)點(diǎn)vp按照劃分規(guī)則處理完后,節(jié)點(diǎn)vA則可能會(huì)變成非關(guān)鍵節(jié)點(diǎn)。如圖5 所示,節(jié)點(diǎn)v3和節(jié)點(diǎn)v4都是關(guān)鍵節(jié)點(diǎn),且節(jié)點(diǎn)v3是節(jié)點(diǎn)v4的父節(jié)點(diǎn)。如果直接根據(jù)關(guān)鍵節(jié)點(diǎn)劃分規(guī)則處理節(jié)點(diǎn)v4,則還需要再處理一次節(jié)點(diǎn)v3,但是事實(shí)上這是沒有必要的,因?yàn)橹恍枰獙⒐?jié)點(diǎn)v3按照關(guān)鍵節(jié)點(diǎn)劃分之后,節(jié)點(diǎn)v4就會(huì)變成非關(guān)鍵節(jié)點(diǎn),所以需要先遞歸處理父節(jié)點(diǎn)中的關(guān)鍵節(jié)點(diǎn)以保證方法的高效性。
圖5 遞歸父節(jié)點(diǎn)劃分示意圖Fig.5 Schematic diagram of dividing parent nodes recursively
2)劃分關(guān)鍵節(jié)點(diǎn)。
對于關(guān)鍵節(jié)點(diǎn)的劃分,根據(jù)節(jié)點(diǎn)類型的不同,其劃分規(guī)則也不相同。
a)對于業(yè)務(wù)功能類型的關(guān)鍵節(jié)點(diǎn),根據(jù)其自身的實(shí)體標(biāo)簽,將其歸類至相同實(shí)體標(biāo)簽的父節(jié)點(diǎn)所處子圖,并將該節(jié)點(diǎn)與不同實(shí)體標(biāo)簽的父節(jié)點(diǎn)連接的邊斷開,完成后更新該節(jié)點(diǎn)及其子節(jié)點(diǎn)的繼承的實(shí)體標(biāo)簽集合。
b)對于工具類的關(guān)鍵節(jié)點(diǎn),本文將其在每個(gè)不同資源的父節(jié)點(diǎn)下都復(fù)制一份,完成后更新該節(jié)點(diǎn)及其子節(jié)點(diǎn)的繼承的實(shí)體標(biāo)簽集合。
基于實(shí)體標(biāo)簽的圖模型劃分算法如算法1所示。
在基于資源約束初步識(shí)別微服務(wù)之后,有些微服務(wù)之間還可能存在著頻繁的依賴關(guān)系,比如微服務(wù)MSb中的類依賴微服務(wù)MSd中的類所提供的數(shù)據(jù)和操作,如果直接部署使用這些依賴頻繁的微服務(wù),將會(huì)增大微服務(wù)MSb中對外接口的使用成本(比如增加了接口調(diào)用的時(shí)間成本),因此可以合并相互依賴頻次高的微服務(wù)以提高其服務(wù)質(zhì)量。
本節(jié)基于微服務(wù)間依賴頻次最小化原則設(shè)置了微服務(wù)對外依賴指標(biāo)。本文把微服務(wù)MSb對微服務(wù)MSd的所有類中的函數(shù)調(diào)用稱之為微服務(wù)MSb對微服務(wù)MSd的依賴值dep(MSb,MSd),并把微服務(wù)MSb對其他微服務(wù)的依賴值的集合定義為dep(MSb)={dep(MSb,MSd)|b∈h,d≠b},其中h表示微服務(wù)的總數(shù)量。當(dāng)微服務(wù)MSb對微服務(wù)MSd的依賴值dep(MSb,MSd)大于微服務(wù)整體的依賴值時(shí),可以認(rèn)為兩個(gè)微服務(wù)之間的緊密程度過高,可合并成一個(gè)微服務(wù),其中微服務(wù)的整體依賴值計(jì)算方法如式(4)所示:
其中,|dep(MSb)|表示MSb對其他微服務(wù)的依賴值的集合中元素的個(gè)數(shù)。
本文實(shí)驗(yàn)的環(huán)境配置如下:處理器為Intel Core i5-6500 CPU@3.20 GHz;內(nèi)存為12 GB;操作系統(tǒng)為Windows 10 企業(yè)版64 位;編譯語言為Python 3.7.0;集成開發(fā)環(huán)境為PyCharm 182.5107.22。
本文實(shí)驗(yàn)使用的數(shù)據(jù)集為來源于GitHub 的4 個(gè)項(xiàng)目:Kanban (https://github.com/eventuate-examples/es-kanbanboard)、Money Transfer(https://github.com/cer/event-sourcingexamples)、Piggy Metrics (https://github.com/sqshq/PiggyMetrics)和Microservice Event Sourcing(https://github.com/chaokunyang/microservices-event-sourcing)。
本節(jié)使用測試項(xiàng)目MES(Microservice Event Souring)進(jìn)行實(shí)例分析。圖6和圖7展示了實(shí)驗(yàn)結(jié)果。
圖6 未過濾合理劃分的類的實(shí)驗(yàn)結(jié)果Fig.6 Experimental results without filtering out reasonably divided classes
圖7 過濾合理劃分的類之后的實(shí)驗(yàn)結(jié)果Fig.7 Experimental results after filtering out reasonably divided classes
作為本文方法的一個(gè)設(shè)計(jì)原則,一個(gè)微服務(wù)不應(yīng)該包含其他微服務(wù)的業(yè)務(wù)類。如果一個(gè)微服務(wù)需要使用其他微服務(wù)的業(yè)務(wù)數(shù)據(jù),可以通過訪問對應(yīng)的業(yè)務(wù)接口來完成數(shù)據(jù)請求,所以圖6 中微服務(wù)User、Shopping Cart、Inventory 和Order 中未包含其他微服務(wù)的業(yè)務(wù)類是合理和符合設(shè)計(jì)原則的,比如微服務(wù)Shopping Cart 中的Order、OrderEevent、OrderEventType 和OrderStatus 四個(gè)類都是屬于微服務(wù)Order 的類,同時(shí)Address和AddressType 這兩個(gè)類在微服務(wù)Inventory 中僅類被Order使用,所以本文方法的劃分中也應(yīng)該是不屬于微服務(wù)Inventory。
此外,通過本文方法識(shí)別得到的微服務(wù)Inventory 中包含的類AddressType 和類BaseEntity 也是屬于合理劃分的,因?yàn)槲⒎?wù)Inventory 中包含了類Address 和類DatabaseInitializer,這兩個(gè)類分別對類AddressType 和類BaseEntity 存在依賴關(guān)系,且類AddressType 和類BaseEntity 屬于非業(yè)務(wù)類,所以其被劃分在微服務(wù)Inventory中是合理的。
在過濾掉上述合理劃分的類之后,重新整理實(shí)驗(yàn)結(jié)果,如圖7 所示。結(jié)合MES 源程序可以發(fā)現(xiàn),微服務(wù)中應(yīng)該包含而沒有出現(xiàn)的類具有以下兩類特征:
1)依賴關(guān)系中未包含該類信息。比如微服務(wù)User 中的ResourceServerConfig、CacheConfig、LoginController和WebMvcConfig 這四個(gè)類,這些類不存在和其他類的依賴關(guān)系,所以類依賴關(guān)系圖中沒有這四個(gè)類的信息。
2)雖然微服務(wù)中未包含該類,但是依賴關(guān)系中存在微服務(wù)中某類對該類的依賴。比如微服務(wù)Order 中的Invoice、InvoiceRepository、InvoiceStatus 和Customer 這四個(gè)類,Invoice、InvoiceRepository 和InvoiceStatus 這三個(gè)類存在相互之間的依賴關(guān)系,但是在原程序的微服務(wù)Order中沒有其他類對其存在依賴關(guān)系,所以本文方法在劃分時(shí)沒有將其劃分到微服務(wù)Order中。同理也沒有將類Customer劃分到微服務(wù)Order中。
而通過對微服務(wù)中原本沒有包含卻出現(xiàn)了的類進(jìn)行分析,發(fā)現(xiàn)微服務(wù)Inventory 之所以包含了類CreditCard 和類CreditCardType 是因?yàn)檫@兩個(gè)不屬于任何一個(gè)實(shí)體類型,而且在微服務(wù)Inventory 中存在對類CreditCard 的依賴關(guān)系,所以本文方法將類CreditCard和類CreditCardType劃分到了微服務(wù)Inventory中。
本文實(shí)驗(yàn)通過正確識(shí)別的類來計(jì)算微服務(wù)劃分準(zhǔn)確率指標(biāo),計(jì)算方法如式(5)所示:
其中:|Cright|表示項(xiàng)目中正確劃分的類的數(shù)量;|Call|表示項(xiàng)目中類的總數(shù)量。
從表1 可以看出,本文方法對各個(gè)實(shí)驗(yàn)測試項(xiàng)目都能取得良好的效果。對Kanban、Money Transfer 和Piggy Metrics 三個(gè)測試項(xiàng)目,本文方法都取得了極高的微服務(wù)劃分準(zhǔn)確率,幾乎和原程序的劃分一致。而對于測試應(yīng)用程序MES,本文方法雖然準(zhǔn)確率略低于其他三個(gè),但是仍具有高于90%的微服務(wù)劃分準(zhǔn)確率。
表1 本文方法的類劃分結(jié)果Tab.1 Results of class division obtained by proposed method
另外,本實(shí)驗(yàn)從微服務(wù)劃分是否合理的角度出發(fā),設(shè)置了微服務(wù)識(shí)別精確率來驗(yàn)證本文所提方法的有效性。該指標(biāo)用來衡量本文方法識(shí)別出來的微服務(wù)中屬于合理微服務(wù)的比例。
圖8 給出了本文方法與文獻(xiàn)[17]方法的對比結(jié)果。從圖8 中可以看出,本文方法的微服務(wù)識(shí)別精確率相較于文獻(xiàn)[17]方法性能更優(yōu)。
最后,實(shí)驗(yàn)還分析了本文方法的性能,即不同測試項(xiàng)目的實(shí)驗(yàn)所需時(shí)間。為排除偶然因素的影響,對每一個(gè)測試項(xiàng)目都重復(fù)50次實(shí)驗(yàn),實(shí)驗(yàn)結(jié)果如圖9所示。其中,圖9(a)縱坐標(biāo)軸表示本文方法執(zhí)行一次所需的時(shí)間,圖9(b)表示各測試項(xiàng)目的類及類依賴關(guān)系的數(shù)量。從圖9 中可以得知,本文方法具有良好的運(yùn)行性能,其執(zhí)行時(shí)間隨著測試項(xiàng)目中的類數(shù)量的增加而增加。同時(shí)由測試項(xiàng)目MES 和測試項(xiàng)目Piggy Metrics 的執(zhí)行時(shí)間對比可知,本文方法的性能同時(shí)還受到類依賴關(guān)系數(shù)量的影響。
圖8 微服務(wù)識(shí)別精確率對比Fig.8 Accuracy comparison of microservices identification
圖9 本文方法的性能實(shí)驗(yàn)結(jié)果Fig.9 Experimental results on performance of proposed method
本文提出了一種資源約束下的基于類依賴關(guān)系的微服務(wù)識(shí)別提取方法。該方法不僅可從遺留軟件代碼中自動(dòng)識(shí)別出合理的微服務(wù),還可幫助開發(fā)人員將源程序中的類劃分至相應(yīng)的微服務(wù)中,進(jìn)一步降低了開發(fā)人員遷移遺留軟件系統(tǒng)的工作量。因?yàn)樵摲椒ㄒ蕾嚨谌介_源工具從源程序中提取類依賴關(guān)系,如果提取出來的類依賴關(guān)系不完全,則會(huì)在一定程度上影響微服務(wù)識(shí)別的結(jié)果。因此,在未來的工作中,我們計(jì)劃進(jìn)一步研究如何提取完整的類依賴關(guān)系,以便進(jìn)一步提高識(shí)別合理的類依賴關(guān)系的準(zhǔn)確性。