在傳統(tǒng)企業(yè)甚至互聯(lián)網(wǎng)企業(yè)中往往存在大量的遺留系統(tǒng),這些遺留系統(tǒng)大多都能夠正常工作,有的可能還運(yùn)行著關(guān)鍵業(yè)務(wù)或者持有核心數(shù)據(jù)。但是,大部分遺留系統(tǒng)通常經(jīng)常存在技術(shù)陳舊、代碼復(fù)雜、難以修改等特點(diǎn)。筆者曾經(jīng)維護(hù)過一個(gè)Perl實(shí)現(xiàn)的網(wǎng)站,在2015年被解耦前,它已經(jīng)工作了十幾年,為公司占領(lǐng)市場(chǎng)立下了汗馬功勞。奈何技術(shù)陳舊,維護(hù)困難,最后在微服務(wù)化過程中慢慢淡出。
創(chuàng)新互聯(lián)建站服務(wù)項(xiàng)目包括繁昌網(wǎng)站建設(shè)、繁昌網(wǎng)站制作、繁昌網(wǎng)頁制作以及繁昌網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,繁昌網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到繁昌省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!可見,隨著時(shí)間的推移,遺留系統(tǒng)的維護(hù)和管理的成本越來越大。在向微服務(wù)架構(gòu)全面轉(zhuǎn)型的過程中,這些遺留系統(tǒng)就像一只只“攔路虎”,阻擋微服務(wù)轉(zhuǎn)型之路。如何在不影響業(yè)務(wù)的同時(shí),以更安全、更高效、更低成本的方式將這些遺留系統(tǒng)進(jìn)行微服務(wù)改造,使之順利融入微服務(wù)架構(gòu),并充分利用到微服務(wù)架構(gòu)的優(yōu)勢(shì)呢?本章將詳細(xì)介紹如何解決遺留系統(tǒng)的微服務(wù)改造問題。
“遺留系統(tǒng)是一種舊的方法、技術(shù)、計(jì)算機(jī)系統(tǒng)或者應(yīng)用程序,它意味著系統(tǒng)過時(shí)了或者需要被取代?!?/p>
這是維基百科上對(duì)于遺留系統(tǒng)的定義,對(duì)于“遺留”的含義有明確闡釋,即過時(shí)或者需要被取代的系統(tǒng)。正因?yàn)槿绱?,遺留系統(tǒng)通常具有一些類似的特征。
龐大的單體應(yīng)用:遺留系統(tǒng)大多是多年積累下來的“巨無霸”系統(tǒng),以單體應(yīng)用形式呈現(xiàn)。
難于修改:多年的代碼累積過程中疏于重構(gòu),導(dǎo)致代碼的可讀性和可擴(kuò)展性較差。
維護(hù)成本很高:在龐大的代碼中尋找bug的根本原因可能會(huì)比較困難。此外,運(yùn)維也是難題,比如在本章開頭提到的Perl系統(tǒng),沒有APM工具支持它的監(jiān)控。
學(xué)習(xí)成本高昂:由于設(shè)計(jì)陳舊或技術(shù)過時(shí),可能了解遺留系統(tǒng)技術(shù)和業(yè)務(wù)的人已經(jīng)很難找到。
缺乏質(zhì)量保障:由于過去缺少投入,許多功能基本上沒有自動(dòng)化測(cè)試來保障質(zhì)量。
這些問題隨著日積月累,可能會(huì)給團(tuán)隊(duì)帶來越來越多的負(fù)擔(dān),直到無法承受其管理和維護(hù)成本。所以遺留系統(tǒng)在微服務(wù)架構(gòu)下如何進(jìn)行改造,是大多數(shù)企業(yè)在面向微服務(wù)轉(zhuǎn)型時(shí)都不得不面對(duì)的問題,而且急需盡早考慮、盡快解決,才能避免問題累積到一定程度后集中爆發(fā)。
2. 直接重寫遺留系統(tǒng)可行么
遇到遺留系統(tǒng)的改造問題時(shí),不少人可能會(huì)首先想到一個(gè)直截了當(dāng)?shù)姆桨福和品貙?,用全新的系統(tǒng)一次性替換掉遺留系統(tǒng)。采用這種方式,在落地過程中總會(huì)發(fā)現(xiàn)種種問題,導(dǎo)致新系統(tǒng)無法順利切換,舊系統(tǒng)又無法完全替代。常見的問題有以下幾種:
上線困難,業(yè)務(wù)阻塞風(fēng)險(xiǎn)高:在遺留系統(tǒng)重寫的過程中,往往會(huì)有新增需求對(duì)遺留系統(tǒng)的功能進(jìn)行修改,在新系統(tǒng)未上線前,這些需求要么被阻塞,要么需要付出雙倍的工作量在遺留系統(tǒng)和新系統(tǒng)上同時(shí)實(shí)現(xiàn),無論哪種選擇對(duì)團(tuán)隊(duì)都是很難接受的。
影響面不可控,系統(tǒng)改造周期長:遺留系統(tǒng)往往運(yùn)行著關(guān)鍵業(yè)務(wù)或者持有核心數(shù)據(jù),會(huì)被多個(gè)上層服務(wù)所調(diào)用。一旦決定開始重寫,其影響面無法評(píng)估,需要極為小心,考慮周全,客觀上會(huì)造成系統(tǒng)改造周期被無限期拉長。
學(xué)習(xí)成本高,知識(shí)傳遞周期長:遺留系統(tǒng)的改造周期越長,對(duì)系統(tǒng)的學(xué)習(xí)成本就會(huì)隨之升高。在這個(gè)過程中隨著人員的正常流失,團(tuán)隊(duì)對(duì)業(yè)務(wù)和技術(shù)的熟悉程度會(huì)逐漸降低,而新人需要花費(fèi)更多的時(shí)間才能熟悉遺留系統(tǒng)改造過程中必要的知識(shí)和技術(shù)。
綜上所述,寄希望于直接重寫遺留系統(tǒng)、進(jìn)行一次性替換而解決微服務(wù)改造問題是不現(xiàn)實(shí)的,必須探索一條能夠持續(xù)可演進(jìn)的道路,實(shí)現(xiàn)快速、低成本、影響面可控的改造效果。
對(duì)遺留系統(tǒng)的改造,既要不影響業(yè)務(wù),實(shí)現(xiàn)平滑、安全的過渡,又要保證能夠高效、穩(wěn)步地推進(jìn),這對(duì)于軟件開發(fā)者來說是個(gè)不小的挑戰(zhàn)。
結(jié)合筆者的經(jīng)驗(yàn),在遺留系統(tǒng)改造過程中可以采取以下思路:
遵循“演進(jìn)式改造流程”,優(yōu)先改造最具價(jià)值的部分,并保證改造過程中的風(fēng)險(xiǎn)可控。
采用“絞殺者模式”,通過逐步替換而非一次性替換的方式,來保證新舊系統(tǒng)的平滑過渡。
采用“挎斗模式”,將不容易改造的遺留系統(tǒng)接入微服務(wù)環(huán)境中。
演進(jìn)式改造流程是一種以逐步演進(jìn)的方式對(duì)遺留系統(tǒng)進(jìn)行改造的流程,其過程如圖6-1所示。
圖6-1 對(duì)遺留系統(tǒng)的演進(jìn)式改造流程
1.1 構(gòu)建服務(wù)路標(biāo)圖
動(dòng)手進(jìn)行改造之前,首先需要構(gòu)造出一個(gè)服務(wù)路標(biāo)圖,來粗粒度地展示在理想情況下所期望的各個(gè)服務(wù)及相互之間的依賴關(guān)系。以該圖作為路標(biāo),指導(dǎo)我們著手進(jìn)行服務(wù)化改造。當(dāng)然,服務(wù)路標(biāo)圖可以不必苛求完整,可以隨著開發(fā)過程的演進(jìn)而不斷完善。構(gòu)建路標(biāo)圖的過程可由架構(gòu)師、業(yè)務(wù)分析師及技術(shù)負(fù)責(zé)人共同參與,構(gòu)建路標(biāo)圖的方法可以參考3.1.1小節(jié)的“服務(wù)拆分策略”。服務(wù)路標(biāo)圖構(gòu)建好之后應(yīng)在團(tuán)隊(duì)中共享并接受來自各個(gè)方面人員的反饋。
1.2 服務(wù)選擇
有了服務(wù)路標(biāo)圖之后,也許會(huì)發(fā)現(xiàn)改造過程千頭萬緒,不知如何選擇合適的部分優(yōu)先進(jìn)行改造。此時(shí)不妨遵循價(jià)值較大化的原則,從多種角度去制定優(yōu)先拆分策略,比如:
優(yōu)先拆分相對(duì)獨(dú)立的部分,獨(dú)立業(yè)務(wù)與舊系統(tǒng)之間的耦合相對(duì)較小,比較容易實(shí)施。
優(yōu)先拆分頻繁變更的部分,可以通過拆分為新的服務(wù)實(shí)現(xiàn)獨(dú)立部署與快速上線,對(duì)于提高團(tuán)隊(duì)總體交付效率價(jià)值較大。
優(yōu)先拆分有特殊資源占用需求的部分,比如將計(jì)算密集型任務(wù)拆分為新服務(wù),并恰當(dāng)?shù)乩迷苹A(chǔ)設(shè)施的彈性伸縮能力對(duì)新服務(wù)實(shí)例進(jìn)行擴(kuò)縮容,有助于提高系統(tǒng)總體性能并降低成本。
1.3 服務(wù)改造
選好優(yōu)先需要改造的部分后,著手將這部分業(yè)務(wù)功能遷移到微服務(wù)架構(gòu)下實(shí)現(xiàn)。改造過程中,通常需要解決這樣的問題:
新舊系統(tǒng)可能需要不同的數(shù)據(jù)源,或具有不同的數(shù)據(jù)庫結(jié)構(gòu),怎樣解決數(shù)據(jù)之間的同步和依賴問題?
單體的舊系統(tǒng)需要拆分為多個(gè)服務(wù)時(shí),怎樣實(shí)現(xiàn)安全的漸進(jìn)式拆分?
根據(jù)改造需求的不同以及數(shù)據(jù)依賴關(guān)系,具體可以分為三種不同的改造場(chǎng)景,每種場(chǎng)景下的技術(shù)實(shí)現(xiàn)各有不同。
1.4 業(yè)務(wù)驗(yàn)證
業(yè)務(wù)功能遷移到微服務(wù)架構(gòu)下實(shí)現(xiàn)后,需要驗(yàn)證新的服務(wù)是否滿足業(yè)務(wù)需求。在新服務(wù)上線投入使用并穩(wěn)定后,可以從遺留系統(tǒng)中移除原有的代碼模塊,如有需要時(shí),一并移除數(shù)據(jù)同步任務(wù)。
1.5 迭代優(yōu)化
至此已經(jīng)對(duì)一部分遺留系統(tǒng)的業(yè)務(wù)完成了微服務(wù)改造,對(duì)于剩余的部分,可以按照類似的方法迭代進(jìn)行,重新審視服務(wù)路標(biāo)圖,選出下一個(gè)需要改造的業(yè)務(wù),繼續(xù)進(jìn)行優(yōu)化,直到完成既定的微服務(wù)改造目標(biāo)。
在微服務(wù)架構(gòu)還未流行起來之前,對(duì)于增量式的大規(guī)模軟件改造,常用的是名為“抽象分支”的方法,如圖6-2所示。
圖6-2 抽象分支
組件解耦:在需要被替換的組件與組件消費(fèi)者之間創(chuàng)建一個(gè)抽象層,這一層只根據(jù)需要聲明抽象的接口或協(xié)議,具體的實(shí)現(xiàn)仍存留于待替換組件中。這樣就通過引入抽象層實(shí)現(xiàn)了待替換組件與組件消費(fèi)者之間的解耦。
新舊組件共存:開發(fā)新組件實(shí)現(xiàn)同一套抽象層接口,并與待替換組件同時(shí)在系統(tǒng)中工作,但開始時(shí)只將少部分功能在新組件中實(shí)現(xiàn),或者只有少部分生產(chǎn)環(huán)境流量導(dǎo)入到新組件上。
逐步替換:隨著功能逐漸完備和技術(shù)逐漸成熟,新組件承擔(dān)越來越多的生產(chǎn)環(huán)境流量。經(jīng)過全面考驗(yàn)后,新組件可以完全替代舊組件之時(shí),舊組件就可以正式下線了。此時(shí)如果有需要也可以移除抽象層。
對(duì)遺留系統(tǒng)的微服務(wù)改造策略,也可以借鑒“抽象分支”的思路,只不過在微服務(wù)架構(gòu)下,抽象層是由一個(gè)獨(dú)立的門面(Facade)服務(wù)實(shí)現(xiàn),該服務(wù)將請(qǐng)求轉(zhuǎn)發(fā)到新系統(tǒng)或者舊系統(tǒng),起到路由作用。
在改造過程中,新舊系統(tǒng)會(huì)同時(shí)存在,共同協(xié)作對(duì)外提供價(jià)值。隨著改造過程的推進(jìn),新系統(tǒng)提供的功能和價(jià)值越來越多,逐步地取代原有遺留系統(tǒng)的功能,用戶流量也會(huì)越來越多地導(dǎo)入到新系統(tǒng)上,最后原有遺留系統(tǒng)不再被使用時(shí),就可以安全地下線了,此時(shí)門面服務(wù)也可以安全地移除。
這種平滑的過渡方法通常被稱為“絞殺者模式”。遺留系統(tǒng)被逐步“絞殺”的過程,如圖6-3所示。
圖6-3 絞殺者模式
絞殺者模式特別適合用于對(duì)復(fù)雜度較高的大型遺留系統(tǒng)進(jìn)行逐步改造,但在遷移過程中也需要注意以下問題:
考慮新系統(tǒng)和遺留系統(tǒng)之間的數(shù)據(jù)共享或者同步方式。
確保絞殺者門面服務(wù)不會(huì)出現(xiàn)單點(diǎn)故障或成為性能“瓶頸”。
傳統(tǒng)企業(yè)中存在大量的遺留系統(tǒng),要對(duì)這些遺留系統(tǒng)全部進(jìn)行微服務(wù)化改造的成本會(huì)很高,并不現(xiàn)實(shí),而且有些遺留系統(tǒng)甚至是無法完全改造的。對(duì)于這些系統(tǒng),我們的選擇并不一定是將其進(jìn)行微服務(wù)化改造,而是將其接入到微服務(wù)環(huán)境中,與其他服務(wù)共同協(xié)作來實(shí)現(xiàn)業(yè)務(wù)需求。
然而將各種形態(tài)各異的遺留系統(tǒng)接入到微服務(wù)環(huán)境中并不容易,存在以下常見問題:
需要對(duì)原有代碼進(jìn)行一定修改,但遺留系統(tǒng)有時(shí)本身已經(jīng)難于修改,而且如果原系統(tǒng)是由多種語言構(gòu)成的,就要為每種語言考慮解決方案。
接入代碼如果是和原系統(tǒng)運(yùn)行在同一進(jìn)程中,就意味著沒有很好的隔離,可能會(huì)因?yàn)榻尤氪a的一點(diǎn)小問題造成原系統(tǒng)無法工作。那么是否存在低成本的方法,將遺留系統(tǒng)接入到微服務(wù)環(huán)境中呢?一種方法是使用挎斗模式,如圖6-4所示?!翱娑贰币辉~來源于帶挎斗的摩托車。
圖6-4 挎斗模式
如圖6-4所示,具體到遺留系統(tǒng)接入場(chǎng)景下,挎斗模式就是將接入功能代碼集中在一起,作為一個(gè)獨(dú)立的進(jìn)程或服務(wù),為不同語言的遺留系統(tǒng)提供一個(gè)同構(gòu)的接入接口。在部署結(jié)構(gòu)上,挎斗服務(wù)與原遺留系統(tǒng)緊密相關(guān),原遺留系統(tǒng)在哪里它就在哪里。對(duì)于原遺留系統(tǒng)應(yīng)用程序的每個(gè)實(shí)例,旁邊都部署和托管了一個(gè)挎斗實(shí)例??娑肥侵С峙c原應(yīng)用一起部署的進(jìn)程或服務(wù)。
使用挎斗模式的好處有以下幾個(gè):
挎斗服務(wù)是獨(dú)立運(yùn)行的進(jìn)程或服務(wù),與原遺留系統(tǒng)的實(shí)現(xiàn)語言無關(guān),不需要為每種語言各開發(fā)一種挎斗。
由于是非侵入式的接入方法,通常不需要改寫原遺留系統(tǒng)的代碼,可以實(shí)現(xiàn)零修改成本的接入。
挎斗服務(wù)與原遺留系統(tǒng)相鄰部署,可以訪問與原系統(tǒng)相同的資源,有時(shí)可以拿來作為監(jiān)控服務(wù)的接入代理。
雖然增加了一些通信成本,但是由于挎斗與原系統(tǒng)相鄰部署,增加的通信成本往往很少,延遲很低。
在使用挎斗模式時(shí),也需要注意以下問題:
注意與原系統(tǒng)相鄰部署,降低通信時(shí)延。
注意進(jìn)程間采用與語言無關(guān)的通信機(jī)制,如REST。
考慮使用容器化的部署方式,比如將跨斗服務(wù)和遺留系統(tǒng)部署在同一個(gè)Pod中。
考慮放入挎斗的功能,是作為單獨(dú)的服務(wù)或是傳統(tǒng)的守護(hù)進(jìn)程運(yùn)行方式。
ServiceMesh就是采用了挎斗模式的思路,在每個(gè)服務(wù)近端部署一個(gè)代理,幫助遺留系統(tǒng)接入ServiceMesh,享受服務(wù)治理帶來的好處。
在進(jìn)行具體的改造前,可能會(huì)遇到如下的挑戰(zhàn):
新舊系統(tǒng)可能需要不同的數(shù)據(jù)源,或具有不同的數(shù)據(jù)庫結(jié)構(gòu),怎樣解決數(shù)據(jù)之間的同步和依賴問題呢?
單體應(yīng)用下的舊系統(tǒng)需要拆分為多個(gè)服務(wù)時(shí),怎樣實(shí)現(xiàn)安全的漸進(jìn)式拆分?
下面根據(jù)遺留系統(tǒng)改造過程中的常見場(chǎng)景,來一一解答這些問題。遺留系統(tǒng)的常見改造場(chǎng)景,如圖6-5所示。
圖6-5遺留系統(tǒng)的常見改造場(chǎng)景
如圖6-5所示,對(duì)于某個(gè)具體改造需求,可以分為以下兩種不同的場(chǎng)景。
實(shí)現(xiàn)新業(yè)務(wù):需要在系統(tǒng)中增加新業(yè)務(wù),與現(xiàn)有業(yè)務(wù)相對(duì)獨(dú)立。根據(jù)新業(yè)務(wù)與現(xiàn)有業(yè)務(wù)之間.
否存在數(shù)據(jù)依賴關(guān)系,又可分為兩種子場(chǎng)景,即與現(xiàn)有業(yè)務(wù)數(shù)據(jù)獨(dú)立或者與現(xiàn)有業(yè)務(wù)數(shù)據(jù)依賴。
對(duì)現(xiàn)有業(yè)務(wù)微服務(wù)化:需要將系統(tǒng)的現(xiàn)有業(yè)務(wù)改造為微服務(wù)架構(gòu),比如對(duì)現(xiàn)有業(yè)務(wù)的拆分和服務(wù)化工作。
適用場(chǎng)景:新業(yè)務(wù)與現(xiàn)有業(yè)務(wù)數(shù)據(jù)獨(dú)立,不存在依賴。
實(shí)現(xiàn)方案:新業(yè)務(wù)可以通過單獨(dú)的服務(wù)和數(shù)據(jù)庫來實(shí)現(xiàn)。在消費(fèi)者請(qǐng)求和底層系統(tǒng)之間引入一個(gè)絞殺者門面服務(wù),該服務(wù)負(fù)責(zé)將請(qǐng)求按照業(yè)務(wù)不同路由到遺留系統(tǒng)和新業(yè)務(wù)服務(wù)上即可,如圖6-6所示。
圖6-6改造場(chǎng)景1:業(yè)務(wù)數(shù)據(jù)獨(dú)立
由于和遺留系統(tǒng)的數(shù)據(jù)之間無耦合,新服務(wù)的技術(shù)棧、工具選型就比較靈活,充分利用到微服務(wù)技術(shù)的異構(gòu)性。
新業(yè)務(wù)與現(xiàn)有業(yè)務(wù)有數(shù)據(jù)依賴時(shí),根據(jù)數(shù)據(jù)持有者不同,有兩種處理方案。
方案A:遺留系統(tǒng)持有數(shù)據(jù),新業(yè)務(wù)訪問共享數(shù)據(jù)。
方案B:新業(yè)務(wù)服務(wù)持有數(shù)據(jù),通過數(shù)據(jù)同步解決數(shù)據(jù)依賴問題。
方案A:遺留系統(tǒng)持有數(shù)據(jù),新業(yè)務(wù)訪問共享數(shù)據(jù)
適用場(chǎng)景:允許新業(yè)務(wù)訪問遺留系統(tǒng)的共享數(shù)據(jù),或者數(shù)據(jù)庫分離成本較高。
實(shí)現(xiàn)方案:在允許新業(yè)務(wù)服務(wù)直接訪問遺留系統(tǒng)的數(shù)據(jù)庫的情況下,最簡單的一種實(shí)現(xiàn)方案是新業(yè)務(wù)服務(wù)直接連接遺留系統(tǒng)數(shù)據(jù)庫獲得數(shù)據(jù),如圖6-7所示。
這種方案的實(shí)施成本相對(duì)較低,與原有數(shù)據(jù)源進(jìn)行集成即可,但缺點(diǎn)也在于此,由于直接使用了數(shù)據(jù)庫作為集成方式,新業(yè)務(wù)服務(wù)仍與原遺留系統(tǒng)數(shù)據(jù)存在直接耦合,原數(shù)據(jù)庫的變動(dòng)會(huì)直接影響到新業(yè)務(wù)服務(wù)的實(shí)現(xiàn),而且對(duì)于新舊業(yè)務(wù)的數(shù)據(jù)訪問權(quán)限與控制也需要納入考慮范圍內(nèi)。針對(duì)這些問題,也可以考慮另外一種方案,即新業(yè)務(wù)服務(wù)通過遺留系統(tǒng)所提供的API來獲得數(shù)據(jù),如圖6-8所示。
圖6-7改造場(chǎng)景2:直接訪問遺留系統(tǒng)數(shù)據(jù)
圖6-8改造場(chǎng)景2:通過API訪問遺留系統(tǒng)數(shù)據(jù)
這種方案的優(yōu)點(diǎn)是可以通過API隔離數(shù)據(jù)變化,避免新業(yè)務(wù)服務(wù)與原遺留系統(tǒng)數(shù)據(jù)之間的直接耦合??梢栽贏PI中實(shí)現(xiàn)對(duì)數(shù)據(jù)的處理邏輯,滿足新業(yè)務(wù)服務(wù)的數(shù)據(jù)需求。API只對(duì)外提供新業(yè)務(wù)服務(wù)允許訪問的數(shù)據(jù)域,從而實(shí)現(xiàn)數(shù)據(jù)權(quán)限的控制,更適用于數(shù)據(jù)庫直接訪問受限制的場(chǎng)景。但其引入的額外成本是需要在原遺留系統(tǒng)上增加API實(shí)現(xiàn)的工作,有時(shí)因?yàn)檫z留系統(tǒng)的技術(shù)陳舊、結(jié)構(gòu)復(fù)雜等原因,會(huì)使得這部分工作比較難于執(zhí)行。
不難看出,方案A中的方法多多少少都對(duì)原系統(tǒng)有一定的侵入性,或與原數(shù)據(jù)庫直接耦合,或需要對(duì)原遺留系統(tǒng)進(jìn)行修改。面向?qū)ο笤O(shè)計(jì)原則中的“開閉原則”,即“對(duì)擴(kuò)展開放,對(duì)修改封閉”,可以幫助我們實(shí)現(xiàn)靈活可擴(kuò)展的軟件架構(gòu),在這里也同樣適用。因此可以考慮在原有系統(tǒng)基礎(chǔ)上進(jìn)行擴(kuò)展,而不是直接修改原遺留系統(tǒng),于是誕生了另一個(gè)方案:新業(yè)務(wù)服務(wù)持有數(shù)據(jù),通過數(shù)據(jù)同步解決數(shù)據(jù)依賴問題。
適用場(chǎng)景:不允許新業(yè)務(wù)訪問遺留系統(tǒng)的共享數(shù)據(jù),或者數(shù)據(jù)庫分離成本不高。
實(shí)現(xiàn)方案:具體實(shí)現(xiàn)方法是建立獨(dú)立的數(shù)據(jù)庫供新業(yè)務(wù)服務(wù)所使用,而新數(shù)據(jù)庫與遺留系統(tǒng)數(shù)據(jù)庫之間通過一個(gè)專門的數(shù)據(jù)同步服務(wù)進(jìn)行同步,將新數(shù)據(jù)庫中的數(shù)據(jù)轉(zhuǎn)化為與遺留系統(tǒng)數(shù)據(jù)可兼容的形式,并遷移過去。數(shù)據(jù)同步服務(wù)又稱ETL(Extract、Transform、Load)服務(wù),其主要職責(zé)是讀取、轉(zhuǎn)換和同步數(shù)據(jù),如圖6-9所示。
圖6-9改造場(chǎng)景2:通過ETL進(jìn)行數(shù)據(jù)同步
該方案的優(yōu)點(diǎn)在于使用了獨(dú)立的數(shù)據(jù)庫,對(duì)原遺留系統(tǒng)的侵入性較低,新業(yè)務(wù)服務(wù)持有新的數(shù)據(jù)庫,與原遺留系統(tǒng)解除了直接耦合,新業(yè)務(wù)服務(wù)和新數(shù)據(jù)庫的選型都可以比較靈活。所有新舊數(shù)據(jù)之間的轉(zhuǎn)換邏輯都在ETL服務(wù)中實(shí)現(xiàn),以后任何數(shù)據(jù)轉(zhuǎn)換與同步的邏輯變化都只與ETL服務(wù)有關(guān),無須再去修改原遺留系統(tǒng)和新業(yè)務(wù)服務(wù)。但該方案帶來的成本是需要額外實(shí)現(xiàn)一個(gè)ETL服務(wù)。另外由于需要在獨(dú)立的兩個(gè)數(shù)據(jù)庫之間進(jìn)行同步,可能只能滿足數(shù)據(jù)的最終一致性而無法做到強(qiáng)一致性。另外也要考慮ETL服務(wù)的可用性,避免引入新的單點(diǎn)故障。
如果進(jìn)一步考慮到數(shù)據(jù)隔離問題,避免直接暴露新服務(wù)的數(shù)據(jù)庫數(shù)據(jù),還可以讓ETL服務(wù)通過新業(yè)務(wù)服務(wù)的API來訪問數(shù)據(jù),如圖6-10所示。這種方案進(jìn)一步解除了ETL服務(wù)與新業(yè)務(wù)數(shù)據(jù)庫之間的耦合,新增的API還可以進(jìn)一步被復(fù)用,作為其他服務(wù)消費(fèi)者訪問數(shù)據(jù)庫的接口。
圖6-10改造場(chǎng)景2:通過ELT訪問新業(yè)務(wù)服務(wù)的API進(jìn)行數(shù)據(jù)同步
大多數(shù)遺留系統(tǒng)在單體應(yīng)用架構(gòu)下已經(jīng)承載了許多關(guān)鍵業(yè)務(wù)或持有核心數(shù)據(jù),對(duì)其進(jìn)行微服務(wù)化改造時(shí),一次性的重構(gòu)風(fēng)險(xiǎn)是比較大的。因此,更為提倡通過數(shù)次小的重構(gòu)來逐步實(shí)現(xiàn)微服務(wù)化改造。
適用場(chǎng)景:對(duì)遺留系統(tǒng)承載的現(xiàn)有業(yè)務(wù)微服務(wù)化,實(shí)現(xiàn)漸進(jìn)式的重構(gòu)。
實(shí)現(xiàn)方案:以一個(gè)含有多模塊的單體應(yīng)用遺留系統(tǒng)改造為例,通過以下三個(gè)步驟拆分為業(yè)務(wù)數(shù)據(jù)分離的兩個(gè)服務(wù)。
1.將內(nèi)部代碼調(diào)用修改為本地REST接口調(diào)用:將被調(diào)函數(shù)修改為REST接口暴露出來,調(diào)用者模塊通過對(duì)本地REST接口調(diào)用完成與原有業(yè)務(wù)等價(jià)的功能。此時(shí)還未拆分服務(wù),仍然是作為一個(gè)服務(wù)整體上線。
2.將本地REST接口調(diào)用改為服務(wù)間REST接口調(diào)用:拆分服務(wù),將原有的調(diào)用者模塊拆分為獨(dú)立服務(wù),REST接口調(diào)用地址改為目標(biāo)接口所在的服務(wù)地址。這一步接口變動(dòng)的成本相當(dāng)小,重點(diǎn)在于讓拆分后的服務(wù)能夠獨(dú)立部署與上線。同時(shí),為避免一次引入過多變更,此階段拆分后的服務(wù)仍然直接訪問原數(shù)據(jù)庫的共享數(shù)據(jù)。
3.業(yè)務(wù)數(shù)據(jù)分離:將拆分后的服務(wù)所需的數(shù)據(jù)分離出來,作為新服務(wù)的獨(dú)享數(shù)據(jù)庫所持有。兩個(gè)數(shù)據(jù)庫之間的數(shù)據(jù)依賴問題,可以借前文所述的數(shù)據(jù)同步方案(ETL服務(wù))解決。
按照上述過程,根據(jù)需要不斷迭代,將原遺留系統(tǒng)的業(yè)務(wù)逐步微服務(wù)化,總體過程的示意圖,如圖6-11所示。
圖6-11改造場(chǎng)景3:對(duì)現(xiàn)有業(yè)務(wù)微服務(wù)化
如何對(duì)遺留系統(tǒng)的數(shù)據(jù)庫進(jìn)行拆分
在上述幾個(gè)改造場(chǎng)景中,有些步驟涉及對(duì)遺留系統(tǒng)的數(shù)據(jù)庫進(jìn)行拆分。那么在多服務(wù)共享數(shù)據(jù)庫的情況下,如何決定首先拆分哪個(gè)服務(wù)的數(shù)據(jù)庫?哪種拆分順序的工作量最小呢?一種方法是采用數(shù)據(jù)“讀依賴最小”的順序進(jìn)行拆分,這種方法的實(shí)施可以概括為以下三個(gè)步驟:
1.表拆分,解除服務(wù)之間的數(shù)據(jù)關(guān)系耦合:列出各個(gè)服務(wù)對(duì)表的讀寫關(guān)系,如果只有一個(gè)服務(wù)對(duì)某個(gè)表進(jìn)行寫操作,不用拆分該表;當(dāng)有多個(gè)服務(wù)對(duì)某個(gè)表進(jìn)行寫操作時(shí),首先考慮將這張表拆分成多張有連接關(guān)系的表,將其轉(zhuǎn)化為被某個(gè)服務(wù)單獨(dú)進(jìn)行寫操作的表。
2.繪制服務(wù)依賴關(guān)系圖:在每張表被獨(dú)立服務(wù)單獨(dú)進(jìn)行寫操作的情況下,按照如下規(guī)則構(gòu)造有向圖,即將所有服務(wù)作為有向圖中的點(diǎn),當(dāng)服務(wù)A需要從服務(wù)B的數(shù)據(jù)庫讀取數(shù)據(jù)時(shí),畫一條由B指向A的有向邊,表示服務(wù)A依賴于服務(wù)B;依此類推,直到繪制完整個(gè)依賴關(guān)系圖。
3.庫拆分,解除服務(wù)之間的數(shù)據(jù)庫耦合:以有向圖中各節(jié)點(diǎn)的出度(即從節(jié)點(diǎn)出發(fā)的邊的條數(shù))作為該服務(wù)被依賴數(shù),進(jìn)行排序,挑選被依賴數(shù)最小的服務(wù),首先對(duì)其進(jìn)行數(shù)據(jù)庫解耦,把該服務(wù)下的數(shù)據(jù)庫表獨(dú)立出來,并在該服務(wù)里提供數(shù)據(jù)接口,以供依賴于它的服務(wù)調(diào)用。
重復(fù)第3步,直到所有數(shù)據(jù)庫被拆分為由各個(gè)服務(wù)獨(dú)享的數(shù)據(jù)庫。
例如,如圖6-12所示,是一組包含四個(gè)服務(wù)的依賴關(guān)系圖,服務(wù)右上角的角標(biāo)表示該服務(wù)的被依賴數(shù)。得知短信服務(wù)的被依賴數(shù)為0,為當(dāng)前被依賴最少的服務(wù),可以首先將該服務(wù)的數(shù)據(jù)庫拆分出來。其次再按順序依次拆分積分服務(wù)、訂單服務(wù)和賬戶服務(wù)的數(shù)據(jù)庫,直至所有數(shù)據(jù)庫被拆分為由服務(wù)所獨(dú)享的數(shù)據(jù)庫。
圖6-12服務(wù)依賴關(guān)系圖示例
本節(jié)以筆者曾親身參與改造的遺留系統(tǒng)為案例,介紹如何將一個(gè)大型遺留系統(tǒng)向微服務(wù)架構(gòu)進(jìn)行遷移。
該系統(tǒng)是一個(gè)用于提供全球房產(chǎn)信息搜索服務(wù)的大型門戶平臺(tái),核心功能主要包括如下幾點(diǎn),如圖6-13所示。
為用戶提供基于地理位置、關(guān)鍵字的房產(chǎn)信息搜索功能。
提供基于定制搜索條件的房源信息訂閱。
提供桌面端、平板端、手機(jī)端等多種不同方式的訪問。
圖6-13系統(tǒng)現(xiàn)狀
該系統(tǒng)是從多年前收購的一個(gè)通用搜索平臺(tái)改造而來的,整體為一個(gè)規(guī)模龐大的單體應(yīng)用,使用同一個(gè)代碼庫,技術(shù)棧主要以Java為主,數(shù)據(jù)庫為Postgre,搜索引擎使用FASTSearch(歷史原因),代碼量大約在300萬行左右。
隨著業(yè)務(wù)演進(jìn)的速度越來越快,單體應(yīng)用的弊端導(dǎo)致系統(tǒng)變更成本越來越高。即使修改幾行代碼也需要經(jīng)過冗長的測(cè)試以及發(fā)布流程,系統(tǒng)發(fā)布才能生效。面對(duì)這些問題,團(tuán)隊(duì)決心使用微服務(wù)架構(gòu),將未來的新功能全部以微服務(wù)實(shí)現(xiàn),并將系統(tǒng)原有功能逐步遷移到微服務(wù)架構(gòu)下。
步驟1:通過遺留系統(tǒng)API提供數(shù)據(jù)
當(dāng)時(shí),研發(fā)團(tuán)隊(duì)接到的第一個(gè)特性需求如下所示:
支持NativeApp提供核心功能
三個(gè)月上線首個(gè)版本
此時(shí),面臨的挑戰(zhàn)主要包括以下兩個(gè):
該系統(tǒng)的主要研發(fā)成員在海外,國內(nèi)團(tuán)隊(duì)剛成立,新人多,知識(shí)遷移成本高。
系統(tǒng)本身是基于商業(yè)通用搜索平臺(tái)上進(jìn)行的二次開發(fā),邏輯復(fù)雜,代碼耦合度高,變更成本高。
采取的方案主要包括如下幾部分:
在遺留系統(tǒng)上封裝現(xiàn)有邏輯,提供基礎(chǔ)API。
重新實(shí)現(xiàn)一個(gè)服務(wù),類似之前章節(jié)提到的BFF模式,為NativeApp提供數(shù)據(jù)。
該服務(wù)通過訪問遺留系統(tǒng)提供的基礎(chǔ)API獲得所需要的數(shù)據(jù)。
該服務(wù)內(nèi)實(shí)現(xiàn)必要的轉(zhuǎn)換,提供給NativeApp界面進(jìn)行呈現(xiàn)。
利用微服務(wù)架構(gòu)的異構(gòu)性,新服務(wù)命名為AppService,使用RubyOnRails實(shí)現(xiàn),并實(shí)現(xiàn)相關(guān)的數(shù)據(jù)轉(zhuǎn)換邏輯,為NativeApp提供數(shù)據(jù)API。通過這種方式實(shí)現(xiàn)的新服務(wù),不僅能滿足NativeApp的需求,而且可以快速開發(fā),獨(dú)立部署。
改造后的系統(tǒng)架構(gòu)圖,如圖6-14所示。其中AppService服務(wù)提供兩組API,分別是提供房源信息的EstateAPI和提供地理位置信息的LocAPI。
圖6-14
步驟1:通過遺留系統(tǒng)API提供數(shù)據(jù)
通過如上所述的方式,極大地減少了對(duì)原門戶平臺(tái)的修改,同時(shí)通過在新服務(wù)上實(shí)現(xiàn)相關(guān)邏輯,不僅提升了交付效率,而且該服務(wù)能夠獨(dú)立部署上線。
步驟2:通過遺留系統(tǒng)數(shù)據(jù)源提供數(shù)據(jù)
接下來,研發(fā)團(tuán)隊(duì)接到的特性需求如下:
支持房產(chǎn)發(fā)布者的信息展示。
支持地理興趣點(diǎn)(POI)信息顯示。
但是這兩部分的數(shù)據(jù)在原門戶平臺(tái)的基礎(chǔ)搜索API中并未包含,在面臨人手有限和交付時(shí)間緊的挑戰(zhàn)下,如果要在門戶平臺(tái)中新增接口,變更成本會(huì)比較高,可能無法按時(shí)交付。
面對(duì)這些挑戰(zhàn),團(tuán)隊(duì)決定盡量避免對(duì)原有遺留系統(tǒng)的直接修改,改為在AppService微服務(wù)中實(shí)現(xiàn)新業(yè)務(wù)邏輯,做了如下修改:
直接訪問遺留系統(tǒng)數(shù)據(jù)源:由于原門戶平臺(tái)的基礎(chǔ)搜索API提供的數(shù)據(jù)不足,便讓AppServic
通過直接訪問遺留系統(tǒng)數(shù)據(jù)源的方式,獲得所需的房源、發(fā)布者、地理興趣點(diǎn)等數(shù)據(jù)信息。
構(gòu)建基礎(chǔ)搜索模塊:在AppService中構(gòu)建基礎(chǔ)搜索模板(EngineAPI),實(shí)現(xiàn)與原門戶平臺(tái)的基礎(chǔ)搜索API同樣的功能,提供數(shù)據(jù)給上層API。
構(gòu)建業(yè)務(wù)層API:在AppService中構(gòu)建業(yè)務(wù)層API,新增兩組API,分別是提供地理興趣點(diǎn)數(shù)據(jù)的POIAPI和房產(chǎn)發(fā)布者信息的PubAPI。
獨(dú)立地理位置服務(wù):將之前相對(duì)獨(dú)立的LocAPI,拆分出來作為獨(dú)立的地理位置服務(wù)LocService。
改造后的系統(tǒng)架構(gòu)圖,如圖6-15所示。通過這步改造,將基礎(chǔ)搜索數(shù)據(jù)的查詢功能重構(gòu)到了AppService微服務(wù)內(nèi),減少了對(duì)原門戶平臺(tái)相關(guān)功能的修改。
步驟3:拆分基礎(chǔ)搜索服務(wù),替換數(shù)據(jù)源
產(chǎn)品的需求持續(xù)演進(jìn),接下來,團(tuán)隊(duì)需要為桌面端用戶新增支持多邊形的地理位置搜索功能。但在現(xiàn)有架構(gòu)下,F(xiàn)ASTSearch搜索引擎并不支持這個(gè)功能,這就意味著需要替換原有的搜索引擎,需要對(duì)原門戶平臺(tái)的代碼做大量的修改。
如何有效地實(shí)現(xiàn)這個(gè)特性呢?具體操作方法如下:
1.將基礎(chǔ)搜索API拆分出來作為獨(dú)立服務(wù)EngineService,這樣以后關(guān)于搜索相關(guān)的邏輯,都可以放在EngineService中實(shí)現(xiàn)了。
圖6-15步驟2:通過遺留系統(tǒng)數(shù)據(jù)源提供數(shù)據(jù)
2. 在EngineService中實(shí)現(xiàn)基于多邊形的搜索。
3. 對(duì)原門戶平臺(tái)的搜索機(jī)制重構(gòu),讓其使用EngineService獲取相關(guān)數(shù)據(jù)(之前是使用FASTSDK)。基于這種方式,門戶平臺(tái)和AppService與底層搜索引擎隔離開來,可以方便地替換底層數(shù)據(jù)源。
該方案的實(shí)現(xiàn),如圖6-16所示。
圖6-16步驟3:拆分基礎(chǔ)搜索服務(wù),替換數(shù)據(jù)源
步驟4:構(gòu)建移動(dòng)端專屬的后端服務(wù)
團(tuán)隊(duì)在預(yù)期時(shí)間內(nèi)很快地完成了前面的各項(xiàng)需求,隨著業(yè)務(wù)發(fā)展迅猛,接下來需要在手機(jī)端和平板端增強(qiáng)用戶體驗(yàn)。然而,目前手機(jī)端、平板端的實(shí)現(xiàn)邏輯較落伍,均是基于JSP的模板方式實(shí)現(xiàn)。如果要直接修改,成本高。
考慮到在屏幕尺寸、性能和顯示限制等方面,移動(dòng)設(shè)備與桌面瀏覽器的能力存在顯著差異。因此,移動(dòng)應(yīng)用對(duì)后端的需求與桌面UI是不一致的。
如果仍然使用原有的門戶平臺(tái)邏輯,則需要同時(shí)為這兩種客戶端提供數(shù)據(jù),這極大地增加了維護(hù)的成本。于是,團(tuán)隊(duì)借鑒了BFF模式,將手機(jī)端和平板端網(wǎng)站的特定后臺(tái)需求,拆分為一個(gè)獨(dú)立的后臺(tái)服務(wù)SPA-Web,作為后端實(shí)現(xiàn)。為前端提供數(shù)據(jù)。然后基于新的前端技術(shù),采用輕量級(jí)、更有效的單頁面方式實(shí)現(xiàn)前端,如圖6-17所示。
圖6-17
步驟4:構(gòu)建移動(dòng)端專屬的后端服務(wù)
通過為移動(dòng)端創(chuàng)建一個(gè)專屬的后端服務(wù),可以為移動(dòng)端的用戶進(jìn)行專門的優(yōu)化,量身打造很好體驗(yàn)。同時(shí),由于微服務(wù)架構(gòu)下的這個(gè)新服務(wù)足夠的小而簡單,就更容易進(jìn)行修改、部署和上線了,也更容易在性能和行為方面進(jìn)行精心的優(yōu)化。
步驟5:基于數(shù)據(jù)繼續(xù)劃分微服務(wù)
隨著團(tuán)隊(duì)的微服務(wù)化實(shí)踐越來越成熟,整個(gè)系統(tǒng)的架構(gòu)趨向于朝更細(xì)的粒度演進(jìn)。
系統(tǒng)典型的業(yè)務(wù)場(chǎng)景分為待售房源(Buy)、已售房源(Sold)、待租房源(Rent)等。所以,團(tuán)隊(duì)基于這些業(yè)務(wù)繼續(xù)劃分單獨(dú)的搜索API以及獨(dú)立的數(shù)據(jù)存儲(chǔ)機(jī)制,每種數(shù)據(jù)由獨(dú)立的ElasticSearch 集群存儲(chǔ),并利用前后端分離的機(jī)制實(shí)現(xiàn)前后端的解耦。
解耦后的系統(tǒng),如圖6-18所示。
圖6-18步驟5:按照數(shù)據(jù)劃分微服務(wù)
最終實(shí)現(xiàn)的服務(wù),如下表所示。
服務(wù)名稱 | 主要描述 |
UserService | 處理用戶信息的認(rèn)證和鑒權(quán) |
SavedSearchService | 處理用戶保存的訂閱條件 |
POIService | 處理POIService相關(guān)的信息 |
LocService | 提供地理位置相關(guān)接口 |
BuyService | 提供待售房源相關(guān)的接口 |
SoldService | 提供已售房源相關(guān)的接口 |
RentService | 處理待租房源相關(guān)的邏輯 |
對(duì)于系統(tǒng)中使用的服務(wù)支撐組件,如服務(wù)注冊(cè)發(fā)現(xiàn)機(jī)制、集中化配置信息等機(jī)制,其實(shí)現(xiàn)和在之前的章節(jié)中闡述的差不多,這里就不展開贅述了。
可以看到,經(jīng)過上面一系列步驟后,原有的門戶平臺(tái)已逐漸遷移為微服務(wù)的系統(tǒng),原有的大約300萬行的代碼也只剩下了大約50萬行,繼續(xù)提供著業(yè)務(wù)價(jià)值。團(tuán)隊(duì)對(duì)遺留系統(tǒng)的修改和變更成本已經(jīng)大大減少,總體交付周期也大大縮短,技術(shù)的升級(jí)帶來性能、可維護(hù)性的提升,充分享受到了微服務(wù)架構(gòu)小而美的好處。
本章介紹了遺留系統(tǒng)的特點(diǎn)、改造策略和場(chǎng)景,并結(jié)合一個(gè)實(shí)戰(zhàn)案例進(jìn)行了講解。目的是幫助讀者從以下方面掌握對(duì)遺留系統(tǒng)的微服務(wù)改造方法:
遺留系統(tǒng)是“需要被替代的系統(tǒng)”,往往存在類似的特征,如難于修改、學(xué)習(xí)和維護(hù)成本高、缺乏質(zhì)量保障等。
通過直接重寫并一次性替換遺留系統(tǒng)解決微服務(wù)改造問題是不現(xiàn)實(shí)的,可能會(huì)導(dǎo)致上線困難、影響面不可控、學(xué)習(xí)成本高等問題的產(chǎn)生。
對(duì)于遺留系統(tǒng)的改造過程,應(yīng)當(dāng)采取逐步替換而非一次性替換的策略。通常采用“演進(jìn)式改造流程”和“絞殺者模式”來保證整個(gè)改造過程可控,并實(shí)現(xiàn)平滑過渡。
另外,對(duì)于遺留系統(tǒng)的改造需求,本章將其細(xì)分為三種場(chǎng)景,如新業(yè)務(wù)數(shù)據(jù)獨(dú)立、新業(yè)務(wù)數(shù)據(jù)依賴以及現(xiàn)有業(yè)務(wù)服務(wù)化。通過對(duì)這些場(chǎng)景的分析,能有效地指導(dǎo)讀者進(jìn)行微服務(wù)的演進(jìn)。
本文節(jié)選自《微服務(wù)架構(gòu)與實(shí)踐(第2版)》一書,王磊等著,電子工業(yè)出版社出版。
當(dāng)前標(biāo)題:從300萬行到50萬行代碼,遺留系統(tǒng)的微服務(wù)改造-創(chuàng)新互聯(lián)
網(wǎng)站地址:http://muchs.cn/article14/ceejge.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、用戶體驗(yàn)、軟件開發(fā)、手機(jī)網(wǎng)站建設(shè)、營銷型網(wǎng)站建設(shè)、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容