Chris Richardson微服務(wù)翻譯:重構(gòu)單體服務(wù)為微服務(wù)

Chris Richardson 微服務(wù)系列翻譯全7篇鏈接:

原文鏈接:Refactoring a Monolith into Microservices


微服務(wù)重構(gòu)的概述

將單體應(yīng)用轉(zhuǎn)化為微服務(wù)是應(yīng)用現(xiàn)代化的一種形式,數(shù)十年來開發(fā)者們一直致力于此。因此,將應(yīng)用重構(gòu)為微服務(wù)時,我們可以借鑒其中的一些經(jīng)驗(yàn)。

首先不要大規(guī)模地重寫代碼,不要集中所有力量從頭構(gòu)建一個新的微服務(wù)應(yīng)用,這中方式聽起來很吸引人,但是會有極大的風(fēng)險(xiǎn),有可能以失敗告終。正如 Martin Fowler 所言:

the only thing a Big Bang rewrite guarantees is a Big Bang!

相反,我們應(yīng)該漸進(jìn)式的重構(gòu)單體應(yīng)用。逐步構(gòu)建由微服務(wù)組成的新應(yīng)用,與單體應(yīng)用一起運(yùn)行;隨著時間的推移,單體應(yīng)用實(shí)現(xiàn)的功能會不斷收縮,直至完全消失或者轉(zhuǎn)變?yōu)榱硪粋€微服務(wù)。這一方法雖然充滿挑戰(zhàn),但風(fēng)險(xiǎn)遠(yuǎn)小于大規(guī)模重寫代碼。

Martin Fowler 將這一策略稱為“殺手應(yīng)用”。這一名稱源自熱帶雨林中的殺手藤。殺手藤附生于大樹的周圍,企圖得到樹冠處的陽光,最后樹木死后,留下樹狀的藤蔓。應(yīng)用現(xiàn)代化也遵循這一方式。我們將圍繞著遺留應(yīng)用構(gòu)建由一些列微服務(wù)組成的新應(yīng)用,直至遺留應(yīng)用消失。

接下來了解不同的實(shí)現(xiàn)策略。

策略一:停止挖坑

如果發(fā)現(xiàn)自己掉入坑里,應(yīng)該馬上停止挖坑。一旦單體應(yīng)用變的難以管理,你應(yīng)該停止讓單體應(yīng)用繼續(xù)變的龐大,實(shí)現(xiàn)新功能時,不應(yīng)該往單體應(yīng)用中添加新的代碼。相反的,而是把新代碼放到獨(dú)立的微服務(wù)中。下圖展示了此方法的系統(tǒng)架構(gòu):

除了新服務(wù)和遺留的單體應(yīng)用,這個系統(tǒng)還包括其他兩個組件:第一個是請求路由,用來處理 HTTP 請求,與之前文章中所說的 API 網(wǎng)關(guān)類似。路由將與新功能相對應(yīng)的請求發(fā)送到新服務(wù)上去,將遺留請求發(fā)送到已有的單體應(yīng)用上去。

另一組件是膠水代碼,用來集成微服務(wù)與單體應(yīng)用。微服務(wù)很少獨(dú)立存在,通常需要訪問單體應(yīng)用擁有的數(shù)據(jù)。膠水代碼存在于單體應(yīng)用或微服務(wù)中,或者兩者兼有,用來負(fù)責(zé)數(shù)據(jù)的集成。微服務(wù)使用膠水代碼來對單體應(yīng)用的數(shù)據(jù)進(jìn)行讀寫。

微服務(wù)可以通過以下三種方式來訪問單體應(yīng)用的數(shù)據(jù):

  • 調(diào)用單體應(yīng)用提供的 API
  • 直接訪問單體應(yīng)用的數(shù)據(jù)庫
  • 維護(hù)一份數(shù)據(jù)副本,與單體應(yīng)用的數(shù)據(jù)庫保持同步

膠水代碼也被稱為 anti-corruption layer。因?yàn)槟z水代碼能防止擁有全新領(lǐng)域模型的服務(wù)被遺留單體應(yīng)用的領(lǐng)域模型所污染。膠水代碼在兩種不同的模型間進(jìn)行轉(zhuǎn)換。anti-corruption layer 這一術(shù)語來自 Eric Evans 撰寫的 Domain Driven Design 中。要想遠(yuǎn)離單體應(yīng)用的泥淖,開發(fā) anti-corruption layer 是必不可少的。

以輕量級微服務(wù)的方式實(shí)現(xiàn)新功能有許多優(yōu)點(diǎn):它能夠防止單體應(yīng)用變的不可管理。微服務(wù)能夠獨(dú)立開發(fā)、部署和擴(kuò)展。你可以通過創(chuàng)建新的服務(wù)來體會到微服務(wù)架構(gòu)的好處。

然而,這一方法并沒有解決單體應(yīng)用的問題。要想解決這些問題,需要拆分單體應(yīng)用。讓我們看一下拆分的策略。

策略二:前后端分離

縮小單體應(yīng)用的策略之一是將展示層從業(yè)務(wù)邏輯層和數(shù)據(jù)訪問層中拆分出來。典型的企業(yè)應(yīng)用包括一下三種組件:

  • 展示層:處理 HTTP 請求并實(shí)現(xiàn)基于 REST API 或 HTML 的 Web UI。在一個-
    用戶界面復(fù)雜的應(yīng)用中,展示層通常包含了大量的代碼
  • 業(yè)務(wù)邏輯層:應(yīng)用的核心,實(shí)現(xiàn)了業(yè)務(wù)邏輯
  • 數(shù)據(jù)訪問層:訪問數(shù)據(jù)庫和消息代理等基礎(chǔ)架構(gòu)組件

通常展示層對于業(yè)務(wù)邏輯層與數(shù)據(jù)訪問層來講,彼此有著清晰的劃分。業(yè)務(wù)層由若干個 API 組成,內(nèi)部封裝了業(yè)務(wù)邏輯。這些 API 是將單體應(yīng)用拆分為兩個更小應(yīng)用的分界線。一個應(yīng)用包含表示層,另一個應(yīng)用包含業(yè)務(wù)邏輯層和數(shù)據(jù)訪問層。拆分后,展示邏輯的應(yīng)用向業(yè)務(wù)邏輯的應(yīng)用發(fā)起遠(yuǎn)程調(diào)用。下圖展示了重構(gòu)前后的構(gòu)架:

以這種方式拆分單體應(yīng)用有兩大好處:1)它使得兩個應(yīng)用可以獨(dú)立的開發(fā)、部署和擴(kuò)展。尤其是,它使得展示層的開發(fā)者能夠快速迭代用戶界面,輕松的進(jìn)行 AB 測試。2)暴露了可被其他服務(wù)調(diào)用的 API
這種策略也只是部分解決方案,很有可能兩個應(yīng)用會變成難以管理的單體應(yīng)用。這時需要使用第三種策略來消除剩余的單體應(yīng)用。

策略三:提取微服務(wù)

重構(gòu)的第三個策略是將單體中現(xiàn)有的模塊變成獨(dú)立的微服務(wù),每次提取模塊為微服務(wù)時,單體就會縮小,一旦轉(zhuǎn)化了足夠多的模塊,單體應(yīng)用將不再是問題,要么消息,要么變成另一個微服務(wù)。

為需要轉(zhuǎn)化為微服務(wù)的模塊設(shè)置優(yōu)先級

大型、復(fù)雜的單體應(yīng)用由數(shù)十甚至數(shù)百個模塊組成,所有模塊都是可提取的。弄清楚哪個模塊需要首先被提取往往是挑戰(zhàn)性的問題。一個好的方式是先選擇易于提取的模塊,這將給開發(fā)者熟悉微服務(wù)以及積累提取經(jīng)驗(yàn)。之后可以提取哪些可以帶來最大收益的模塊。

將模塊轉(zhuǎn)變?yōu)槲⒎?wù)通常需要一定時間,一般會根據(jù)獲得收益的大小來給模塊排序。通常轉(zhuǎn)換經(jīng)常變化的模塊帶來的收益也最大。一旦把一個模塊轉(zhuǎn)換為微服務(wù),就可以獨(dú)立開發(fā)、部署它了,從而加快了開發(fā)速度。

提取那些對資源有獨(dú)特需求的模塊也會帶來很多好處。例如,把需要內(nèi)存數(shù)據(jù)庫的模塊轉(zhuǎn)化為微服務(wù),就能部署在大內(nèi)存的主機(jī)上。同樣的,將實(shí)現(xiàn)計(jì)算密集型算法的模塊提取出來也是值得的,該微服務(wù)可以部署在擁有多個 CPU 的主機(jī)上。這種方式使得應(yīng)用更容易擴(kuò)展。

當(dāng)決定哪些模塊需要提取時,找出現(xiàn)有粗粒度的邊界(即分界線)也大有裨益。這會使模塊轉(zhuǎn)化為微服務(wù)更加簡單、省力。例如:一個只通過異步消息與其他部分通信的模塊,轉(zhuǎn)化為微服務(wù)是更簡單的。

如何提取模塊

提取模塊的第一步是確定模塊和單體應(yīng)用的接口粒度。單體應(yīng)用和微服務(wù)需要訪問彼此的數(shù)據(jù),更像是雙向 API,模塊和應(yīng)用其它部分之間存在著互相依賴,因此實(shí)現(xiàn)這些 API 一般充滿挑戰(zhàn)。重構(gòu)時使用領(lǐng)域模型來實(shí)現(xiàn)業(yè)務(wù)邏輯變的尤為困難,一般需要大的代碼改動才能打破這些依賴。

一旦實(shí)現(xiàn)粗粒度的接口,就可以將模塊轉(zhuǎn)化為獨(dú)立的微服務(wù)。要做到這一點(diǎn),必須能夠讓單體應(yīng)用和微服務(wù)通過 API 通信。 下圖展示了重構(gòu)前、重構(gòu)中和重構(gòu)后的不同架構(gòu):

模塊 Z 是要被提取的模塊,它使用到模塊 Y ,同時它的組件被模塊 X 使用。重構(gòu)的第一步就是定義一組粗粒度 API,第一個接口是模塊 X 調(diào)用模塊 Z 的 接口。第二個接口是模塊 Z 調(diào)用模塊 Y的接口。

重構(gòu)的第二步是把模塊轉(zhuǎn)變?yōu)楠?dú)立的微服務(wù)。對內(nèi)和對外接口通過 IPC 機(jī)制實(shí)現(xiàn),開發(fā)人員可能只需要將模塊 Z 與微服務(wù)支撐框架(Microservice Chassis framework)組合起來構(gòu)建微服務(wù)。

一旦將模塊提取完畢,你就擁有了一個新的微服務(wù),它能夠獨(dú)立于單體應(yīng)用和其它微服務(wù)進(jìn)行開發(fā)、部署和擴(kuò)展。如果想重寫微服務(wù)的代碼,集成微服務(wù)和單體應(yīng)用的 API 會成為這兩個領(lǐng)域模型之間的 anti-corruption layer。每提取一個模塊,就向著微服務(wù)的方向又邁進(jìn)了一步。隨著時間推移,單體應(yīng)用將會逐漸消失,你也會擁有更多的微服務(wù)。

總結(jié)

將單體應(yīng)用遷移到微服務(wù)的過程是應(yīng)用現(xiàn)代化的一種形式。并不需要從頭重寫代碼,而是漸進(jìn)式地將應(yīng)用重構(gòu)為一組微服務(wù)。其中有三種策略:使用微服務(wù)實(shí)現(xiàn)新功能;將展示層從業(yè)務(wù)邏輯層、數(shù)據(jù)訪問層中拆分;將單體應(yīng)用內(nèi)的模塊轉(zhuǎn)化為微服務(wù)。隨著時間的推移,微服務(wù)的數(shù)量將會增加,從而提升團(tuán)隊(duì)的敏捷和效率。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容