重構(gòu)單體應(yīng)用到微服務(wù)

原文鏈接:Refactoring a Monolith into Microservices

  1. 微服務(wù)介紹
  2. 構(gòu)建微服務(wù)之使用API網(wǎng)關(guān)
  3. 構(gòu)建微服務(wù)之:微服務(wù)架構(gòu)中的進(jìn)程間通信
  4. 微服務(wù)中的服務(wù)發(fā)現(xiàn)
  5. 微服務(wù)之事件驅(qū)動的數(shù)據(jù)管理
  6. 選擇一種微服務(wù)部署策略
  7. 重構(gòu)單體應(yīng)用到微服務(wù)(本文)

這是使用微服務(wù)架構(gòu)構(gòu)建應(yīng)用系列的第七篇也是最后一篇文章,第一篇文章介紹了微服務(wù)架構(gòu)模式,并討論了使用微服務(wù)架構(gòu)的優(yōu)勢和劣勢,接下來的文章討論微服務(wù)架構(gòu)的不同方面:使用API網(wǎng)關(guān)、進(jìn)程間通信、服務(wù)發(fā)現(xiàn)、事件驅(qū)動的數(shù)據(jù)管理以及部署微服務(wù),本篇文章,讓我們看下如何把一個單體應(yīng)用重構(gòu)為微服務(wù)架構(gòu)的應(yīng)用。

我希望這個系列的文章使你對微服務(wù)架構(gòu)有一些好的理解,比如它的優(yōu)勢和劣勢,何時使用微服務(wù)等 ,或許微服務(wù)架構(gòu)對您的組織將非常合適。

然而,你現(xiàn)在更可能正在為一個龐大的、復(fù)雜的單體應(yīng)用而工作,你每天正經(jīng)歷著開發(fā)和部署單體應(yīng)用的緩慢和痛苦 ,微服務(wù)看起來更像一個遙遠(yuǎn)的極樂世界。幸運(yùn)的是,我們有幾個策略可以使你逃離單體的地獄。本篇文章我將描述如何漸進(jìn)的把單體應(yīng)用重構(gòu)為一系列的微服務(wù)。

重構(gòu)為微服務(wù)架構(gòu)的概覽

把一個單體應(yīng)用轉(zhuǎn)化為微服務(wù)實(shí)際是應(yīng)用現(xiàn)代化的一種形式,這個事情開發(fā)者已經(jīng)做了十多年了,因此,有一些經(jīng)驗(yàn)在我們重構(gòu)應(yīng)用為微服務(wù)時候可以重用。

策略之一是不要使用“Big Bang”式的重寫,也就是不要集中所有的力量從頭構(gòu)建一個新的基于微服務(wù)的應(yīng)用,盡管那個方式聽起來很誘人,實(shí)際會有極大的風(fēng)險,最終也會以失敗告終。正如 Martin Fowler 所說:“the only thing a Big Bang rewrite guarantees is a Big Bang!”。

避免使用Big Bang重寫,我們應(yīng)該漸進(jìn)式的重構(gòu)我們的單體應(yīng)用,我們逐步的構(gòu)建由微服務(wù)組成的新應(yīng)用,并與我們的單體應(yīng)用一起運(yùn)行。隨著時間的推移,單體應(yīng)用實(shí)現(xiàn)的功能將會縮水,直到完全消失或者變成另外一個微服務(wù)。這種策略可以類比于在高速路上只把車開到70邁-有挑戰(zhàn)性但是比Big Bang重寫危險小。

Martin Fowler 提到了這種應(yīng)用現(xiàn)代化的策略,稱其為Strangler Application,名稱來源于熱帶雨林中的扼殺藤蔓,扼殺藤蔓生長在大樹的周圍企圖得到樹冠處的陽光,最后樹木會死掉,只留下一堆樹狀的藤蔓。應(yīng)用現(xiàn)代化 遵循這樣的模式,我們將會圍繞的遺留應(yīng)用構(gòu)建由一系列微服務(wù)組成的新的應(yīng)用,最終遺留應(yīng)用將會消失。

Paste_Image.png

讓我們來看實(shí)現(xiàn)該目標(biāo)可采用的不同策略:

策略一:停止挖掘

Law of Holes 告訴我們一旦你落入洞穴,你應(yīng)該停止繼續(xù)挖洞!一旦你的單體應(yīng)用變的難以管理,這是一個需要聽取的極好的建議。換句話講,你應(yīng)該停止讓單體應(yīng)用繼續(xù)變的更加龐大,這意味著,當(dāng)你需要實(shí)現(xiàn)新功能的時候,你不應(yīng)該往單體應(yīng)用中添加新的代碼。相反的,這個策略的重要一點(diǎn)是,把新代碼放到一個獨(dú)立的微服務(wù)中去。下圖展示了應(yīng)用該方法后的系統(tǒng)架構(gòu):

Paste_Image.png

除了新服務(wù)和遺留的單體應(yīng)用,還多出來其他的兩個組件:第一個組件是請求路由器,用來處理HTTP請求,這和我們前面所說的API網(wǎng)關(guān)類似,路由器發(fā)送請求到對應(yīng)新功能的新服務(wù)上去,路由遺留應(yīng)用的請求到單體應(yīng)用中去。

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

服務(wù)有三種策略可以用來訪問單體的數(shù)據(jù):

  • 調(diào)用單體提供的遠(yuǎn)程API
  • 直接訪問單體擁有的數(shù)據(jù)庫
  • 維護(hù)自己的數(shù)據(jù)副本,副本需要從單體應(yīng)用端同步過來

膠水代碼有時也被稱為anti-corruption layer,這是因?yàn)槟z水代碼防止了擁有自己純凈領(lǐng)域模型的服務(wù)被來自遺留單體應(yīng)用的領(lǐng)域模型的設(shè)計理念所污染。膠水代碼在兩種不同的模型間進(jìn)行轉(zhuǎn)換。anti-corruption layer一詞首次出現(xiàn)在Eric Evans 所著的必讀之書 Domain Driven Design 中,并在white paper中被提煉修正。開發(fā)一個anti-corruption layer不是一項(xiàng)簡單的事情,但如果不想陷入單一地獄,還是有必要搞一個的。

把新功能實(shí)現(xiàn)為輕量級的服務(wù)有諸多優(yōu)勢:它避免單體應(yīng)用最終變的不可管理,服務(wù)同時可以被獨(dú)立于單體去開發(fā)、部署和擴(kuò)展。你可以通過創(chuàng)建每一個新的服務(wù)體會到微服務(wù)架構(gòu)的優(yōu)勢。

然而,這種方式并沒有解決單體中的問題,為了解決這些問題,你需要拆分單體。讓我們看一下拆分的策略:

策略二:前后端分離

縮小單體應(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ù)規(guī)則的組件
  • 數(shù)據(jù)訪問層:訪問諸如數(shù)據(jù)庫和消息中介等基礎(chǔ)架構(gòu)的組件。

通常展示邏輯對于后臺業(yè)務(wù)邏輯與數(shù)據(jù)訪問邏輯來講,彼此有清晰的劃分。業(yè)務(wù)層有由一個或多個門面組成的粗粒度API,它封裝了業(yè)務(wù)邏輯組件,這些API是拆分單體應(yīng)用到兩個更小應(yīng)用時候的自然縫隙。一個應(yīng)用包含展示層,另一應(yīng)用包含業(yè)務(wù)邏輯和數(shù)據(jù)訪問邏輯,經(jīng)過拆分,展示邏輯的應(yīng)用向業(yè)務(wù)邏輯應(yīng)用發(fā)起遠(yuǎn)程調(diào)用,下圖展示了重構(gòu)前后的架構(gòu):


Paste_Image.png

這種方式拆分單體應(yīng)用有兩個大的優(yōu)勢:它使得兩個應(yīng)用可以獨(dú)立的開發(fā)、部署和擴(kuò)展,尤其是它允許展示層開發(fā)者快速迭代用戶界面并容易的進(jìn)行A|B測試,這種方式的另一優(yōu)勢是它暴露了可以被其他微服務(wù)調(diào)用的遠(yuǎn)程API。

這種策略,也僅僅是部分解決方案,這樣重構(gòu)完之后,很可能兩個應(yīng)用逐步變成兩個不可管理的單體。你需要使用第三者策略消除剩余的單體部分。

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

重構(gòu)第三個策略是把單體中存在的模塊變成獨(dú)立的微服務(wù)。每一次你提取模塊并把其轉(zhuǎn)化為服務(wù),單體就會縮小,一旦你覆蓋了足夠多的模塊,單體就將不再是一個問題,單體要么消失掉要么小到變成另外一個微服務(wù)。

確定需要轉(zhuǎn)化為服務(wù)的模塊的優(yōu)先級

一個龐大、復(fù)雜的單體應(yīng)用有數(shù)十甚至上百個模塊組成,所有的模塊都是需要提取的候選。確定哪一個模塊需要首先被提取是很有挑戰(zhàn)性的問題,一個好的方式是先選擇容易提取的模塊作為開始,這將給你一些微服務(wù)總體上概覽以及尤其是提取過程上的經(jīng)驗(yàn)。在這之后,你可以提取那些可以給你帶來最大優(yōu)勢的模塊。

把一個模塊轉(zhuǎn)化為一個服務(wù)通常是需要一定時間的。你想要根據(jù)你可以獲得優(yōu)勢的大小排列你的模塊,通常轉(zhuǎn)換經(jīng)常變化的模塊帶來的優(yōu)勢最大。一旦你把一個模塊轉(zhuǎn)化為服務(wù),你就可以獨(dú)立于單體來開發(fā)、部署它 了,這會極大的加速你的開發(fā)效率。

提取那些對資源有獨(dú)特需求的模塊也會帶來很多優(yōu)勢,比如,把擁有內(nèi)存數(shù)據(jù)的模塊轉(zhuǎn)化為服務(wù),就可以把服務(wù)部署到擁有大量內(nèi)存的主機(jī)上,同樣的,提取一個需要實(shí)現(xiàn)計算密集型算法的模塊也是很值得的,因?yàn)樵摲?wù)可以被部署到有多顆CUP的主機(jī)上,通過把有獨(dú)特資源需求的模塊轉(zhuǎn)化為服務(wù),可以使得應(yīng)用更容易擴(kuò)展。

當(dāng)決定哪個模塊需要提取時,查看已存在的粗粒度的邊界(也就是縫隙)是很有用的,這會使模塊轉(zhuǎn)化到服務(wù)更加簡單和低廉。邊界的例子之一是,一個模塊只通過異步消息與應(yīng)用的其他部分進(jìn)行通信,把該模塊轉(zhuǎn)化成微服務(wù)是相當(dāng)廉價和簡單的。

如何提取模塊

提取模塊的第一步是確定模塊與單體應(yīng)用間的粗粒度接口,由于單體和模塊需要訪問彼此擁有的數(shù)據(jù),一般是一些雙向的API。由于依賴的錯綜復(fù)雜以及模塊與應(yīng)用其他部分間細(xì)粒度的交互,實(shí)現(xiàn)這些API一般非常有挑戰(zhàn)性。重構(gòu)使用領(lǐng)域?qū)ο竽J?/a> 設(shè)計的業(yè)務(wù)邏輯尤其困難,因?yàn)轭I(lǐng)域模型類中彼此包含大量的關(guān)聯(lián)關(guān)系 。你一般需要進(jìn)行大的代碼改動才能打破這些依賴,下圖展示了重構(gòu)過程:

一旦你實(shí)現(xiàn)了粗粒度的接口,你就可以把這些模塊轉(zhuǎn)化為獨(dú)立的服務(wù)。為了實(shí)現(xiàn)這個目標(biāo),你必須寫代碼使得單體應(yīng)用和服務(wù)可以通過進(jìn)程間通信機(jī)制的API進(jìn)行交互。下圖展示了應(yīng)用重構(gòu)前、中、后的架構(gòu):


Paste_Image.png

在這個例子中,模塊Z是要被提取的候選模塊,它的組件被模塊X使用,它本身使用到模塊Y。重構(gòu)第一步是定義一組粗粒度的API,第一個接口是模塊X調(diào)用模塊Z的入端接口,第二個接口是模塊Z調(diào)用模塊Y的出端接口。

重構(gòu)的第二步是把模塊轉(zhuǎn)化為獨(dú)立的服務(wù),出端和入端的接口使用IPC機(jī)制的代碼實(shí)現(xiàn),你最有可能需要通過模塊 Z 結(jié)合 Microservice Chassis framework來處理諸如服務(wù)發(fā)現(xiàn)這樣的橫切關(guān)注點(diǎn)。

一旦你提取了某個模塊,你就擁有了另一個可以獨(dú)立于單體應(yīng)用和其他服務(wù)來開發(fā)、部署、擴(kuò)展的新服務(wù),你甚至可以從頭重寫這個服務(wù); 在這種狀況下,API代碼集成單體和微服務(wù),變成了用以轉(zhuǎn)換兩種領(lǐng)域模型的anti-corruption層。每次你提取一個模塊,你就又向微服務(wù)的方向邁出一步,隨著時間的推移,單體應(yīng)用將會縮水,你也會擁有更多的微服務(wù)。

總結(jié)

從一個現(xiàn)存應(yīng)用遷移到微服務(wù)的過程是應(yīng)用現(xiàn)代化的一種形式,你不應(yīng)該以從頭完全重寫的方式把現(xiàn)存應(yīng)用變?yōu)槲⒎?wù),相反的,你應(yīng)該逐步的把應(yīng)用重構(gòu)為一系列的微服務(wù)。你可以使用三種策略:使用微服務(wù)實(shí)現(xiàn)新的功能;把表現(xiàn)層從業(yè)務(wù)邏輯和數(shù)據(jù)訪問組件中拆分出來;把單體應(yīng)用中的現(xiàn)有模塊轉(zhuǎn)化為服務(wù)。隨著時間推移,微服務(wù)的數(shù)量將會增長,你團(tuán)隊(duì)的敏捷性和開發(fā)速度也會提升。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容