小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

從300萬行到50萬行代碼,遺留系統(tǒng)的微服務改造

 黃爸爸好 2019-07-16

在傳統(tǒng)企業(yè)甚至互聯(lián)網(wǎng)企業(yè)中往往存在大量的遺留系統(tǒng),這些遺留系統(tǒng)大多都能夠正常工作,有的可能還運行著關鍵業(yè)務或者持有核心數(shù)據(jù)。但是,大部分遺留系統(tǒng)通常經(jīng)常存在技術陳舊、代碼復雜、難以修改等特點。筆者曾經(jīng)維護過一個Perl實現(xiàn)的網(wǎng)站,在2015年被解耦前,它已經(jīng)工作了十幾年,為公司占領市場立下了汗馬功勞。奈何技術陳舊,維護困難,最后在微服務化過程中慢慢淡出。

可見,隨著時間的推移,遺留系統(tǒng)的維護和管理的成本越來越大。在向微服務架構全面轉(zhuǎn)型的過程中,這些遺留系統(tǒng)就像一只只“攔路虎”,阻擋微服務轉(zhuǎn)型之路。如何在不影響業(yè)務的同時,以更安全、更高效、更低成本的方式將這些遺留系統(tǒng)進行微服務改造,使之順利融入微服務架構,并充分利用到微服務架構的優(yōu)勢呢?本章將詳細介紹如何解決遺留系統(tǒng)的微服務改造問題。

一、遺留系統(tǒng)綜述

1. 什么是遺留系統(tǒng)

“遺留系統(tǒng)是一種舊的方法、技術、計算機系統(tǒng)或者應用程序,它意味著系統(tǒng)過時了或者需要被取代。”

這是維基百科上對于遺留系統(tǒng)的定義,對于“遺留”的含義有明確闡釋,即過時或者需要被取代的系統(tǒng)。正因為如此,遺留系統(tǒng)通常具有一些類似的特征。

  • 龐大的單體應用:遺留系統(tǒng)大多是多年積累下來的“巨無霸”系統(tǒng),以單體應用形式呈現(xiàn)。

  • 難于修改:多年的代碼累積過程中疏于重構,導致代碼的可讀性和可擴展性較差。

  • 維護成本很高:在龐大的代碼中尋找bug的根本原因可能會比較困難。此外,運維也是難題,比如在本章開頭提到的Perl系統(tǒng),沒有APM工具支持它的監(jiān)控。

  • 學習成本高昂:由于設計陳舊或技術過時,可能了解遺留系統(tǒng)技術和業(yè)務的人已經(jīng)很難找到。

  • 缺乏質(zhì)量保障:由于過去缺少投入,許多功能基本上沒有自動化測試來保障質(zhì)量。

這些問題隨著日積月累,可能會給團隊帶來越來越多的負擔,直到無法承受其管理和維護成本。所以遺留系統(tǒng)在微服務架構下如何進行改造,是大多數(shù)企業(yè)在面向微服務轉(zhuǎn)型時都不得不面對的問題,而且急需盡早考慮、盡快解決,才能避免問題累積到一定程度后集中爆發(fā)。

2. 直接重寫遺留系統(tǒng)可行么

遇到遺留系統(tǒng)的改造問題時,不少人可能會首先想到一個直截了當?shù)姆桨福和品貙?,用全新的系統(tǒng)一次性替換掉遺留系統(tǒng)。采用這種方式,在落地過程中總會發(fā)現(xiàn)種種問題,導致新系統(tǒng)無法順利切換,舊系統(tǒng)又無法完全替代。常見的問題有以下幾種:

  • 上線困難,業(yè)務阻塞風險高:在遺留系統(tǒng)重寫的過程中,往往會有新增需求對遺留系統(tǒng)的功能進行修改,在新系統(tǒng)未上線前,這些需求要么被阻塞,要么需要付出雙倍的工作量在遺留系統(tǒng)和新系統(tǒng)上同時實現(xiàn),無論哪種選擇對團隊都是很難接受的。

  • 影響面不可控,系統(tǒng)改造周期長:遺留系統(tǒng)往往運行著關鍵業(yè)務或者持有核心數(shù)據(jù),會被多個上層服務所調(diào)用。一旦決定開始重寫,其影響面無法評估,需要極為小心,考慮周全,客觀上會造成系統(tǒng)改造周期被無限期拉長。

  • 學習成本高,知識傳遞周期長:遺留系統(tǒng)的改造周期越長,對系統(tǒng)的學習成本就會隨之升高。在這個過程中隨著人員的正常流失,團隊對業(yè)務和技術的熟悉程度會逐漸降低,而新人需要花費更多的時間才能熟悉遺留系統(tǒng)改造過程中必要的知識和技術。

綜上所述,寄希望于直接重寫遺留系統(tǒng)、進行一次性替換而解決微服務改造問題是不現(xiàn)實的,必須探索一條能夠持續(xù)可演進的道路,實現(xiàn)快速、低成本、影響面可控的改造效果。

二、遺留系統(tǒng)改造策略

對遺留系統(tǒng)的改造,既要不影響業(yè)務,實現(xiàn)平滑、安全的過渡,又要保證能夠高效、穩(wěn)步地推進,這對于軟件開發(fā)者來說是個不小的挑戰(zhàn)。

結合筆者的經(jīng)驗,在遺留系統(tǒng)改造過程中可以采取以下思路:

  • 遵循“演進式改造流程”,優(yōu)先改造最具價值的部分,并保證改造過程中的風險可控。

  • 采用“絞殺者模式”,通過逐步替換而非一次性替換的方式,來保證新舊系統(tǒng)的平滑過渡。

  • 采用“挎斗模式”,將不容易改造的遺留系統(tǒng)接入微服務環(huán)境中。


1. 演進式改造流

演進式改造流程是一種以逐步演進的方式對遺留系統(tǒng)進行改造的流程,其過程如圖6-1所示。

圖6-1  對遺留系統(tǒng)的演進式改造流程

1.1 構建服務路標圖

動手進行改造之前,首先需要構造出一個服務路標圖,來粗粒度地展示在理想情況下所期望的各個服務及相互之間的依賴關系。以該圖作為路標,指導我們著手進行服務化改造。當然,服務路標圖可以不必苛求完整,可以隨著開發(fā)過程的演進而不斷完善。構建路標圖的過程可由架構師、業(yè)務分析師及技術負責人共同參與,構建路標圖的方法可以參考3.1.1小節(jié)的“服務拆分策略”。服務路標圖構建好之后應在團隊中共享并接受來自各個方面人員的反饋。

1.2 服務選擇

有了服務路標圖之后,也許會發(fā)現(xiàn)改造過程千頭萬緒,不知如何選擇合適的部分優(yōu)先進行改造。此時不妨遵循價值最大化的原則,從多種角度去制定優(yōu)先拆分策略,比如:

  • 優(yōu)先拆分相對獨立的部分,獨立業(yè)務與舊系統(tǒng)之間的耦合相對較小,比較容易實施。

  • 優(yōu)先拆分頻繁變更的部分,可以通過拆分為新的服務實現(xiàn)獨立部署與快速上線,對于提高團隊總體交付效率價值較大。

  • 優(yōu)先拆分有特殊資源占用需求的部分,比如將計算密集型任務拆分為新服務,并恰當?shù)乩迷苹A設施的彈性伸縮能力對新服務實例進行擴縮容,有助于提高系統(tǒng)總體性能并降低成本。

1.3 服務改造

選好優(yōu)先需要改造的部分后,著手將這部分業(yè)務功能遷移到微服務架構下實現(xiàn)。改造過程中,通常需要解決這樣的問題:

  • 新舊系統(tǒng)可能需要不同的數(shù)據(jù)源,或具有不同的數(shù)據(jù)庫結構,怎樣解決數(shù)據(jù)之間的同步和依賴問題?

  • 單體的舊系統(tǒng)需要拆分為多個服務時,怎樣實現(xiàn)安全的漸進式拆分?

根據(jù)改造需求的不同以及數(shù)據(jù)依賴關系,具體可以分為三種不同的改造場景,每種場景下的技術實現(xiàn)各有不同。

1.4 業(yè)務驗證

業(yè)務功能遷移到微服務架構下實現(xiàn)后,需要驗證新的服務是否滿足業(yè)務需求。在新服務上線投入使用并穩(wěn)定后,可以從遺留系統(tǒng)中移除原有的代碼模塊,如有需要時,一并移除數(shù)據(jù)同步任務。

1.5 迭代優(yōu)化

至此已經(jīng)對一部分遺留系統(tǒng)的業(yè)務完成了微服務改造,對于剩余的部分,可以按照類似的方法迭代進行,重新審視服務路標圖,選出下一個需要改造的業(yè)務,繼續(xù)進行優(yōu)化,直到完成既定的微服務改造目標。

2. 絞殺者模式

在微服務架構還未流行起來之前,對于增量式的大規(guī)模軟件改造,常用的是名為“抽象分支”的方法,如圖6-2所示。

圖6-2  抽象分支

?

組件解耦:在需要被替換的組件與組件消費者之間創(chuàng)建一個抽象層,這一層只根據(jù)需要聲明抽象的接口或協(xié)議,具體的實現(xiàn)仍存留于待替換組件中。這樣就通過引入抽象層實現(xiàn)了待替換組件與組件消費者之間的解耦。

  • 新舊組件共存:開發(fā)新組件實現(xiàn)同一套抽象層接口,并與待替換組件同時在系統(tǒng)中工作,但開始時只將少部分功能在新組件中實現(xiàn),或者只有少部分生產(chǎn)環(huán)境流量導入到新組件上。

  • 逐步替換:隨著功能逐漸完備和技術逐漸成熟,新組件承擔越來越多的生產(chǎn)環(huán)境流量。經(jīng)過全面考驗后,新組件可以完全替代舊組件之時,舊組件就可以正式下線了。此時如果有需要也可以移除抽象層。

對遺留系統(tǒng)的微服務改造策略,也可以借鑒“抽象分支”的思路,只不過在微服務架構下,抽象層是由一個獨立的門面(Facade)服務實現(xiàn),該服務將請求轉(zhuǎn)發(fā)到新系統(tǒng)或者舊系統(tǒng),起到路由作用。

在改造過程中,新舊系統(tǒng)會同時存在,共同協(xié)作對外提供價值。隨著改造過程的推進,新系統(tǒng)提供的功能和價值越來越多,逐步地取代原有遺留系統(tǒng)的功能,用戶流量也會越來越多地導入到新系統(tǒng)上,最后原有遺留系統(tǒng)不再被使用時,就可以安全地下線了,此時門面服務也可以安全地移除。

這種平滑的過渡方法通常被稱為“絞殺者模式”。遺留系統(tǒng)被逐步“絞殺”的過程,如圖6-3所示。

圖6-3  絞殺者模式

絞殺者模式特別適合用于對復雜度較高的大型遺留系統(tǒng)進行逐步改造,但在遷移過程中也需要注意以下問題:

  • 考慮新系統(tǒng)和遺留系統(tǒng)之間的數(shù)據(jù)共享或者同步方式。

  • 確保絞殺者門面服務不會出現(xiàn)單點故障或成為性能“瓶頸”。


3. 挎斗模式

傳統(tǒng)企業(yè)中存在大量的遺留系統(tǒng),要對這些遺留系統(tǒng)全部進行微服務化改造的成本會很高,并不現(xiàn)實,而且有些遺留系統(tǒng)甚至是無法完全改造的。對于這些系統(tǒng),我們的選擇并不一定是將其進行微服務化改造,而是將其接入到微服務環(huán)境中,與其他服務共同協(xié)作來實現(xiàn)業(yè)務需求。

  • 然而將各種形態(tài)各異的遺留系統(tǒng)接入到微服務環(huán)境中并不容易,存在以下常見問題:

  • 需要對原有代碼進行一定修改,但遺留系統(tǒng)有時本身已經(jīng)難于修改,而且如果原系統(tǒng)是由多種語言構成的,就要為每種語言考慮解決方案。

接入代碼如果是和原系統(tǒng)運行在同一進程中,就意味著沒有很好的隔離,可能會因為接入代碼的一點小問題造成原系統(tǒng)無法工作。那么是否存在低成本的方法,將遺留系統(tǒng)接入到微服務環(huán)境中呢?一種方法是使用挎斗模式,如圖6-4所示。“挎斗”一詞來源于帶挎斗的摩托車。

圖6-4  挎斗模式

如圖6-4所示,具體到遺留系統(tǒng)接入場景下,挎斗模式就是將接入功能代碼集中在一起,作為一個獨立的進程或服務,為不同語言的遺留系統(tǒng)提供一個同構的接入接口。在部署結構上,挎斗服務與原遺留系統(tǒng)緊密相關,原遺留系統(tǒng)在哪里它就在哪里。對于原遺留系統(tǒng)應用程序的每個實例,旁邊都部署和托管了一個挎斗實例??娑肥侵С峙c原應用一起部署的進程或服務。

使用挎斗模式的好處有以下幾個:

  • 挎斗服務是獨立運行的進程或服務,與原遺留系統(tǒng)的實現(xiàn)語言無關,不需要為每種語言各開發(fā)一種挎斗。

  • 由于是非侵入式的接入方法,通常不需要改寫原遺留系統(tǒng)的代碼,可以實現(xiàn)零修改成本的接入。

  • 挎斗服務與原遺留系統(tǒng)相鄰部署,可以訪問與原系統(tǒng)相同的資源,有時可以拿來作為監(jiān)控服務的接入代理。

  • 雖然增加了一些通信成本,但是由于挎斗與原系統(tǒng)相鄰部署,增加的通信成本往往很少,延遲很低。

在使用挎斗模式時,也需要注意以下問題:

  • 注意與原系統(tǒng)相鄰部署,降低通信時延。

  • 注意進程間采用與語言無關的通信機制,如REST。

  • 考慮使用容器化的部署方式,比如將跨斗服務和遺留系統(tǒng)部署在同一個Pod中。

  • 考慮放入挎斗的功能,是作為單獨的服務或是傳統(tǒng)的守護進程運行方式。

ServiceMesh就是采用了挎斗模式的思路,在每個服務近端部署一個代理,幫助遺留系統(tǒng)接入ServiceMesh,享受服務治理帶來的好處。


三、遺留系統(tǒng)改造場景

在進行具體的改造前,可能會遇到如下的挑戰(zhàn):

  • 新舊系統(tǒng)可能需要不同的數(shù)據(jù)源,或具有不同的數(shù)據(jù)庫結構,怎樣解決數(shù)據(jù)之間的同步和依賴問題呢?

  • 單體應用下的舊系統(tǒng)需要拆分為多個服務時,怎樣實現(xiàn)安全的漸進式拆分?

下面根據(jù)遺留系統(tǒng)改造過程中的常見場景,來一一解答這些問題。遺留系統(tǒng)的常見改造場景,如圖6-5所示。

圖6-5遺留系統(tǒng)的常見改造場景

如圖6-5所示,對于某個具體改造需求,可以分為以下兩種不同的場景。

  • 實現(xiàn)新業(yè)務:需要在系統(tǒng)中增加新業(yè)務,與現(xiàn)有業(yè)務相對獨立。根據(jù)新業(yè)務與現(xiàn)有業(yè)務之間.

  • 否存在數(shù)據(jù)依賴關系,又可分為兩種子場景,即與現(xiàn)有業(yè)務數(shù)據(jù)獨立或者與現(xiàn)有業(yè)務數(shù)據(jù)依賴。

  • 對現(xiàn)有業(yè)務微服務化:需要將系統(tǒng)的現(xiàn)有業(yè)務改造為微服務架構,比如對現(xiàn)有業(yè)務的拆分和服務化工作。


改造場景1:實現(xiàn)新業(yè)務時與現(xiàn)有業(yè)務數(shù)據(jù)獨立

適用場景:新業(yè)務與現(xiàn)有業(yè)務數(shù)據(jù)獨立,不存在依賴。

實現(xiàn)方案:新業(yè)務可以通過單獨的服務和數(shù)據(jù)庫來實現(xiàn)。在消費者請求和底層系統(tǒng)之間引入一個絞殺者門面服務,該服務負責將請求按照業(yè)務不同路由到遺留系統(tǒng)和新業(yè)務服務上即可,如圖6-6所示。

圖6-6改造場景1:業(yè)務數(shù)據(jù)獨立

由于和遺留系統(tǒng)的數(shù)據(jù)之間無耦合,新服務的技術棧、工具選型就比較靈活,充分利用到微服務技術的異構性。

改造場景2:實現(xiàn)新業(yè)務時與現(xiàn)有業(yè)務數(shù)據(jù)依賴

新業(yè)務與現(xiàn)有業(yè)務有數(shù)據(jù)依賴時,根據(jù)數(shù)據(jù)持有者不同,有兩種處理方案。

  • 方案A:遺留系統(tǒng)持有數(shù)據(jù),新業(yè)務訪問共享數(shù)據(jù)。

  • 方案B:新業(yè)務服務持有數(shù)據(jù),通過數(shù)據(jù)同步解決數(shù)據(jù)依賴問題。

方案A:遺留系統(tǒng)持有數(shù)據(jù),新業(yè)務訪問共享數(shù)據(jù)

適用場景:允許新業(yè)務訪問遺留系統(tǒng)的共享數(shù)據(jù),或者數(shù)據(jù)庫分離成本較高。

實現(xiàn)方案:在允許新業(yè)務服務直接訪問遺留系統(tǒng)的數(shù)據(jù)庫的情況下,最簡單的一種實現(xiàn)方案是新業(yè)務服務直接連接遺留系統(tǒng)數(shù)據(jù)庫獲得數(shù)據(jù),如圖6-7所示。

這種方案的實施成本相對較低,與原有數(shù)據(jù)源進行集成即可,但缺點也在于此,由于直接使用了數(shù)據(jù)庫作為集成方式,新業(yè)務服務仍與原遺留系統(tǒng)數(shù)據(jù)存在直接耦合,原數(shù)據(jù)庫的變動會直接影響到新業(yè)務服務的實現(xiàn),而且對于新舊業(yè)務的數(shù)據(jù)訪問權限與控制也需要納入考慮范圍內(nèi)。針對這些問題,也可以考慮另外一種方案,即新業(yè)務服務通過遺留系統(tǒng)所提供的API來獲得數(shù)據(jù),如圖6-8所示。

圖6-7改造場景2:直接訪問遺留系統(tǒng)數(shù)據(jù)

圖6-8改造場景2:通過API訪問遺留系統(tǒng)數(shù)據(jù)

這種方案的優(yōu)點是可以通過API隔離數(shù)據(jù)變化,避免新業(yè)務服務與原遺留系統(tǒng)數(shù)據(jù)之間的直接耦合。可以在API中實現(xiàn)對數(shù)據(jù)的處理邏輯,滿足新業(yè)務服務的數(shù)據(jù)需求。API只對外提供新業(yè)務服務允許訪問的數(shù)據(jù)域,從而實現(xiàn)數(shù)據(jù)權限的控制,更適用于數(shù)據(jù)庫直接訪問受限制的場景。但其引入的額外成本是需要在原遺留系統(tǒng)上增加API實現(xiàn)的工作,有時因為遺留系統(tǒng)的技術陳舊、結構復雜等原因,會使得這部分工作比較難于執(zhí)行。

方案B:新業(yè)務服務持有數(shù)據(jù),通過數(shù)據(jù)同步解決數(shù)據(jù)依賴問題

不難看出,方案A中的方法多多少少都對原系統(tǒng)有一定的侵入性,或與原數(shù)據(jù)庫直接耦合,或需要對原遺留系統(tǒng)進行修改。面向?qū)ο笤O計原則中的“開閉原則”,即“對擴展開放,對修改封閉”,可以幫助我們實現(xiàn)靈活可擴展的軟件架構,在這里也同樣適用。因此可以考慮在原有系統(tǒng)基礎上進行擴展,而不是直接修改原遺留系統(tǒng),于是誕生了另一個方案:新業(yè)務服務持有數(shù)據(jù),通過數(shù)據(jù)同步解決數(shù)據(jù)依賴問題。

適用場景:不允許新業(yè)務訪問遺留系統(tǒng)的共享數(shù)據(jù),或者數(shù)據(jù)庫分離成本不高。

實現(xiàn)方案:具體實現(xiàn)方法是建立獨立的數(shù)據(jù)庫供新業(yè)務服務所使用,而新數(shù)據(jù)庫與遺留系統(tǒng)數(shù)據(jù)庫之間通過一個專門的數(shù)據(jù)同步服務進行同步,將新數(shù)據(jù)庫中的數(shù)據(jù)轉(zhuǎn)化為與遺留系統(tǒng)數(shù)據(jù)可兼容的形式,并遷移過去。數(shù)據(jù)同步服務又稱ETL(Extract、Transform、Load)服務,其主要職責是讀取、轉(zhuǎn)換和同步數(shù)據(jù),如圖6-9所示。

圖6-9改造場景2:通過ETL進行數(shù)據(jù)同步

該方案的優(yōu)點在于使用了獨立的數(shù)據(jù)庫,對原遺留系統(tǒng)的侵入性較低,新業(yè)務服務持有新的數(shù)據(jù)庫,與原遺留系統(tǒng)解除了直接耦合,新業(yè)務服務和新數(shù)據(jù)庫的選型都可以比較靈活。所有新舊數(shù)據(jù)之間的轉(zhuǎn)換邏輯都在ETL服務中實現(xiàn),以后任何數(shù)據(jù)轉(zhuǎn)換與同步的邏輯變化都只與ETL服務有關,無須再去修改原遺留系統(tǒng)和新業(yè)務服務。但該方案帶來的成本是需要額外實現(xiàn)一個ETL服務。另外由于需要在獨立的兩個數(shù)據(jù)庫之間進行同步,可能只能滿足數(shù)據(jù)的最終一致性而無法做到強一致性。另外也要考慮ETL服務的可用性,避免引入新的單點故障。

如果進一步考慮到數(shù)據(jù)隔離問題,避免直接暴露新服務的數(shù)據(jù)庫數(shù)據(jù),還可以讓ETL服務通過新業(yè)務服務的API來訪問數(shù)據(jù),如圖6-10所示。這種方案進一步解除了ETL服務與新業(yè)務數(shù)據(jù)庫之間的耦合,新增的API還可以進一步被復用,作為其他服務消費者訪問數(shù)據(jù)庫的接口。

圖6-10改造場景2:通過ELT訪問新業(yè)務服務的API進行數(shù)據(jù)同步


改造場景3:對現(xiàn)有業(yè)務微服務化

大多數(shù)遺留系統(tǒng)在單體應用架構下已經(jīng)承載了許多關鍵業(yè)務或持有核心數(shù)據(jù),對其進行微服務化改造時,一次性的重構風險是比較大的。因此,更為提倡通過數(shù)次小的重構來逐步實現(xiàn)微服務化改造。

適用場景:對遺留系統(tǒng)承載的現(xiàn)有業(yè)務微服務化,實現(xiàn)漸進式的重構。

實現(xiàn)方案:以一個含有多模塊的單體應用遺留系統(tǒng)改造為例,通過以下三個步驟拆分為業(yè)務數(shù)據(jù)分離的兩個服務。

1.將內(nèi)部代碼調(diào)用修改為本地REST接口調(diào)用:將被調(diào)函數(shù)修改為REST接口暴露出來,調(diào)用者模塊通過對本地REST接口調(diào)用完成與原有業(yè)務等價的功能。此時還未拆分服務,仍然是作為一個服務整體上線。

2.將本地REST接口調(diào)用改為服務間REST接口調(diào)用:拆分服務,將原有的調(diào)用者模塊拆分為獨立服務,REST接口調(diào)用地址改為目標接口所在的服務地址。這一步接口變動的成本相當小,重點在于讓拆分后的服務能夠獨立部署與上線。同時,為避免一次引入過多變更,此階段拆分后的服務仍然直接訪問原數(shù)據(jù)庫的共享數(shù)據(jù)。

3.業(yè)務數(shù)據(jù)分離:將拆分后的服務所需的數(shù)據(jù)分離出來,作為新服務的獨享數(shù)據(jù)庫所持有。兩個數(shù)據(jù)庫之間的數(shù)據(jù)依賴問題,可以借前文所述的數(shù)據(jù)同步方案(ETL服務)解決。

按照上述過程,根據(jù)需要不斷迭代,將原遺留系統(tǒng)的業(yè)務逐步微服務化,總體過程的示意圖,如圖6-11所示。

圖6-11改造場景3:對現(xiàn)有業(yè)務微服務化

如何對遺留系統(tǒng)的數(shù)據(jù)庫進行拆分

在上述幾個改造場景中,有些步驟涉及對遺留系統(tǒng)的數(shù)據(jù)庫進行拆分。那么在多服務共享數(shù)據(jù)庫的情況下,如何決定首先拆分哪個服務的數(shù)據(jù)庫?哪種拆分順序的工作量最小呢?一種方法是采用數(shù)據(jù)“讀依賴最小”的順序進行拆分,這種方法的實施可以概括為以下三個步驟:

1.表拆分,解除服務之間的數(shù)據(jù)關系耦合:列出各個服務對表的讀寫關系,如果只有一個服務對某個表進行寫操作,不用拆分該表;當有多個服務對某個表進行寫操作時,首先考慮將這張表拆分成多張有連接關系的表,將其轉(zhuǎn)化為被某個服務單獨進行寫操作的表。

2.繪制服務依賴關系圖:在每張表被獨立服務單獨進行寫操作的情況下,按照如下規(guī)則構造有向圖,即將所有服務作為有向圖中的點,當服務A需要從服務B的數(shù)據(jù)庫讀取數(shù)據(jù)時,畫一條由B指向A的有向邊,表示服務A依賴于服務B;依此類推,直到繪制完整個依賴關系圖。

3.庫拆分,解除服務之間的數(shù)據(jù)庫耦合:以有向圖中各節(jié)點的出度(即從節(jié)點出發(fā)的邊的條數(shù))作為該服務被依賴數(shù),進行排序,挑選被依賴數(shù)最小的服務,首先對其進行數(shù)據(jù)庫解耦,把該服務下的數(shù)據(jù)庫表獨立出來,并在該服務里提供數(shù)據(jù)接口,以供依賴于它的服務調(diào)用。

重復第3步,直到所有數(shù)據(jù)庫被拆分為由各個服務獨享的數(shù)據(jù)庫。

例如,如圖6-12所示,是一組包含四個服務的依賴關系圖,服務右上角的角標表示該服務的被依賴數(shù)。得知短信服務的被依賴數(shù)為0,為當前被依賴最少的服務,可以首先將該服務的數(shù)據(jù)庫拆分出來。其次再按順序依次拆分積分服務、訂單服務和賬戶服務的數(shù)據(jù)庫,直至所有數(shù)據(jù)庫被拆分為由服務所獨享的數(shù)據(jù)庫。

圖6-12服務依賴關系圖示例


四、遺留系統(tǒng)改造案例

本節(jié)以筆者曾親身參與改造的遺留系統(tǒng)為案例,介紹如何將一個大型遺留系統(tǒng)向微服務架構進行遷移。

1. 改造前的系統(tǒng)情況

該系統(tǒng)是一個用于提供全球房產(chǎn)信息搜索服務的大型門戶平臺,核心功能主要包括如下幾點,如圖6-13所示。

  • 為用戶提供基于地理位置、關鍵字的房產(chǎn)信息搜索功能。

  • 提供基于定制搜索條件的房源信息訂閱。

  • 提供桌面端、平板端、手機端等多種不同方式的訪問。

圖6-13系統(tǒng)現(xiàn)狀

該系統(tǒng)是從多年前收購的一個通用搜索平臺改造而來的,整體為一個規(guī)模龐大的單體應用,使用同一個代碼庫,技術棧主要以Java為主,數(shù)據(jù)庫為Postgre,搜索引擎使用FASTSearch(歷史原因),代碼量大約在300萬行左右。

隨著業(yè)務演進的速度越來越快,單體應用的弊端導致系統(tǒng)變更成本越來越高。即使修改幾行代碼也需要經(jīng)過冗長的測試以及發(fā)布流程,系統(tǒng)發(fā)布才能生效。面對這些問題,團隊決心使用微服務架構,將未來的新功能全部以微服務實現(xiàn),并將系統(tǒng)原有功能逐步遷移到微服務架構下。


2. 改造過程

步驟1:通過遺留系統(tǒng)API提供數(shù)據(jù)

當時,研發(fā)團隊接到的第一個特性需求如下所示:

  • 支持NativeApp提供核心功能

  • 三個月上線首個版本

此時,面臨的挑戰(zhàn)主要包括以下兩個:

  • 該系統(tǒng)的主要研發(fā)成員在海外,國內(nèi)團隊剛成立,新人多,知識遷移成本高。

  • 系統(tǒng)本身是基于商業(yè)通用搜索平臺上進行的二次開發(fā),邏輯復雜,代碼耦合度高,變更成本高。

采取的方案主要包括如下幾部分:

  • 在遺留系統(tǒng)上封裝現(xiàn)有邏輯,提供基礎API。

  • 重新實現(xiàn)一個服務,類似之前章節(jié)提到的BFF模式,為NativeApp提供數(shù)據(jù)。

  • 該服務通過訪問遺留系統(tǒng)提供的基礎API獲得所需要的數(shù)據(jù)。

  • 該服務內(nèi)實現(xiàn)必要的轉(zhuǎn)換,提供給NativeApp界面進行呈現(xiàn)。

利用微服務架構的異構性,新服務命名為AppService,使用RubyOnRails實現(xiàn),并實現(xiàn)相關的數(shù)據(jù)轉(zhuǎn)換邏輯,為NativeApp提供數(shù)據(jù)API。通過這種方式實現(xiàn)的新服務,不僅能滿足NativeApp的需求,而且可以快速開發(fā),獨立部署。

改造后的系統(tǒng)架構圖,如圖6-14所示。其中AppService服務提供兩組API,分別是提供房源信息的EstateAPI和提供地理位置信息的LocAPI。

圖6-14

步驟1:通過遺留系統(tǒng)API提供數(shù)據(jù)

通過如上所述的方式,極大地減少了對原門戶平臺的修改,同時通過在新服務上實現(xiàn)相關邏輯,不僅提升了交付效率,而且該服務能夠獨立部署上線。

步驟2:通過遺留系統(tǒng)數(shù)據(jù)源提供數(shù)據(jù)

接下來,研發(fā)團隊接到的特性需求如下:

  • 支持房產(chǎn)發(fā)布者的信息展示。

  • 支持地理興趣點(POI)信息顯示。

但是這兩部分的數(shù)據(jù)在原門戶平臺的基礎搜索API中并未包含,在面臨人手有限和交付時間緊的挑戰(zhàn)下,如果要在門戶平臺中新增接口,變更成本會比較高,可能無法按時交付。

面對這些挑戰(zhàn),團隊決定盡量避免對原有遺留系統(tǒng)的直接修改,改為在AppService微服務中實現(xiàn)新業(yè)務邏輯,做了如下修改:

  • 直接訪問遺留系統(tǒng)數(shù)據(jù)源:由于原門戶平臺的基礎搜索API提供的數(shù)據(jù)不足,便讓AppServic

  • 通過直接訪問遺留系統(tǒng)數(shù)據(jù)源的方式,獲得所需的房源、發(fā)布者、地理興趣點等數(shù)據(jù)信息。

  • 構建基礎搜索模塊:在AppService中構建基礎搜索模板(EngineAPI),實現(xiàn)與原門戶平臺的基礎搜索API同樣的功能,提供數(shù)據(jù)給上層API。

  • 構建業(yè)務層API:在AppService中構建業(yè)務層API,新增兩組API,分別是提供地理興趣點數(shù)據(jù)的POIAPI和房產(chǎn)發(fā)布者信息的PubAPI。

  • 獨立地理位置服務:將之前相對獨立的LocAPI,拆分出來作為獨立的地理位置服務LocService。

改造后的系統(tǒng)架構圖,如圖6-15所示。通過這步改造,將基礎搜索數(shù)據(jù)的查詢功能重構到了AppService微服務內(nèi),減少了對原門戶平臺相關功能的修改。

步驟3:拆分基礎搜索服務,替換數(shù)據(jù)源

產(chǎn)品的需求持續(xù)演進,接下來,團隊需要為桌面端用戶新增支持多邊形的地理位置搜索功能。但在現(xiàn)有架構下,F(xiàn)ASTSearch搜索引擎并不支持這個功能,這就意味著需要替換原有的搜索引擎,需要對原門戶平臺的代碼做大量的修改。

如何有效地實現(xiàn)這個特性呢?具體操作方法如下:

1.將基礎搜索API拆分出來作為獨立服務EngineService,這樣以后關于搜索相關的邏輯,都可以放在EngineService中實現(xiàn)了。

圖6-15步驟2:通過遺留系統(tǒng)數(shù)據(jù)源提供數(shù)據(jù)

2. 在EngineService中實現(xiàn)基于多邊形的搜索。

3. 對原門戶平臺的搜索機制重構,讓其使用EngineService獲取相關數(shù)據(jù)(之前是使用FASTSDK)?;谶@種方式,門戶平臺和AppService與底層搜索引擎隔離開來,可以方便地替換底層數(shù)據(jù)源。

該方案的實現(xiàn),如圖6-16所示。

圖6-16步驟3:拆分基礎搜索服務,替換數(shù)據(jù)源

步驟4:構建移動端專屬的后端服務

團隊在預期時間內(nèi)很快地完成了前面的各項需求,隨著業(yè)務發(fā)展迅猛,接下來需要在手機端和平板端增強用戶體驗。然而,目前手機端、平板端的實現(xiàn)邏輯較落伍,均是基于JSP的模板方式實現(xiàn)。如果要直接修改,成本高。

考慮到在屏幕尺寸、性能和顯示限制等方面,移動設備與桌面瀏覽器的能力存在顯著差異。因此,移動應用對后端的需求與桌面UI是不一致的。

如果仍然使用原有的門戶平臺邏輯,則需要同時為這兩種客戶端提供數(shù)據(jù),這極大地增加了維護的成本。于是,團隊借鑒了BFF模式,將手機端和平板端網(wǎng)站的特定后臺需求,拆分為一個獨立的后臺服務SPA-Web,作為后端實現(xiàn)。為前端提供數(shù)據(jù)。然后基于最新的前端技術,采用輕量級、更有效的單頁面方式實現(xiàn)前端,如圖6-17所示。

圖6-17

驟4:構建移動端專屬的后端服務

通過為移動端創(chuàng)建一個專屬的后端服務,可以為移動端的用戶進行專門的優(yōu)化,量身打造最佳體驗。同時,由于微服務架構下的這個新服務足夠的小而簡單,就更容易進行修改、部署和上線了,也更容易在性能和行為方面進行精心的優(yōu)化。

步驟5:基于數(shù)據(jù)繼續(xù)劃分微服務

隨著團隊的微服務化實踐越來越成熟,整個系統(tǒng)的架構趨向于朝更細的粒度演進。

系統(tǒng)典型的業(yè)務場景分為待售房源(Buy)、已售房源(Sold)、待租房源(Rent)等。所以,團隊基于這些業(yè)務繼續(xù)劃分單獨的搜索API以及獨立的數(shù)據(jù)存儲機制,每種數(shù)據(jù)由獨立的ElasticSearch 集群存儲,并利用前后端分離的機制實現(xiàn)前后端的解耦。

解耦后的系統(tǒng),如圖6-18所示。

圖6-18步驟5:按照數(shù)據(jù)劃分微服務

最終實現(xiàn)的服務,如下表所示。

服務名稱

主要描述

UserService

處理用戶信息的認證和鑒權

SavedSearchService

處理用戶保存的訂閱條件

POIService

處理POIService相關的信息

LocService

提供地理位置相關接口

BuyService

提供待售房源相關的接口

SoldService

提供已售房源相關的接口

RentService

處理待租房源相關的邏輯

對于系統(tǒng)中使用的服務支撐組件,如服務注冊發(fā)現(xiàn)機制、集中化配置信息等機制,其實現(xiàn)和在之前的章節(jié)中闡述的差不多,這里就不展開贅述了。

3. 改造結果

可以看到,經(jīng)過上面一系列步驟后,原有的門戶平臺已逐漸遷移為微服務的系統(tǒng),原有的大約300萬行的代碼也只剩下了大約50萬行,繼續(xù)提供著業(yè)務價值。團隊對遺留系統(tǒng)的修改和變更成本已經(jīng)大大減少,總體交付周期也大大縮短,技術的升級帶來性能、可維護性的提升,充分享受到了微服務架構小而美的好處。

小結

本章介紹了遺留系統(tǒng)的特點、改造策略和場景,并結合一個實戰(zhàn)案例進行了講解。目的是幫助讀者從以下方面掌握對遺留系統(tǒng)的微服務改造方法:

  • 遺留系統(tǒng)是“需要被替代的系統(tǒng)”,往往存在類似的特征,如難于修改、學習和維護成本高、缺乏質(zhì)量保障等。

  • 通過直接重寫并一次性替換遺留系統(tǒng)解決微服務改造問題是不現(xiàn)實的,可能會導致上線困難、影響面不可控、學習成本高等問題的產(chǎn)生。

  • 對于遺留系統(tǒng)的改造過程,應當采取逐步替換而非一次性替換的策略。通常采用“演進式改造流程”和“絞殺者模式”來保證整個改造過程可控,并實現(xiàn)平滑過渡。

另外,對于遺留系統(tǒng)的改造需求,本章將其細分為三種場景,如新業(yè)務數(shù)據(jù)獨立、新業(yè)務數(shù)據(jù)依賴以及現(xiàn)有業(yè)務服務化。通過對這些場景的分析,能有效地指導讀者進行微服務的演進。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多