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