你頗有可能正在處理大型複雜的單體應用程序,天天開發和部署應用程序的經歷都很緩慢並且很痛苦。微服務看起來很是適合你的應用程序,但它也更像是一項高不可攀的必殺技。如何才能走上微服務架構的道路?下面將介紹一些策略,幫你擺脫單體地獄,而無須從頭開始重寫你的應用程序。前端
經過開發所謂的絞殺者應用程序(strangler application),能夠逐步將單體架構轉換爲微服務架構。絞殺者應用程序的想法來自絞殺式藤蔓,這些藤蔓在雨林中生長,它們包圍繞樹木生成,甚至有時會殺死樹木。絞殺者應用程序是一個由微服務組成的新應用程序,經過將新功能做爲服務,並逐步從單體應用程序中提取服務來實現。隨着時間的推移,當絞殺者應用程序實現愈來愈多的功能時,它會縮小並最終消滅單體應用程序。開發絞殺者應用程序的一個重要好處是,與宇宙大爆炸式的完全重寫不一樣,它能夠馬上落地,更快爲企業提供價值。數據庫
有三種主要策略能夠實現對單體的「絞殺」,並逐步用微服務替換之:後端
1) 將新功能實現爲服務。 2)隔隔表現層和後端。 3) 經過將功能提取到服務中來分解單體。架構
第一種策略阻止了單體的發展。它一般是一種快速展現微服務價值的方法,有助於讓遷移和重構的工做得到公司內部各個層面支持。另外兩種策略打破了單體。在重構單體時,你有時可能會使用第二種策略,但你確定會使用第三種策略,由於它能實現將功能從單體遷移到絞殺者應用程序中。app
下面讓咱們來看一看這些策略。框架
1.將新功能實現爲服務微服務
「挖坑法則」(The Law of Holes)指出:若是你發現本身已經陷入了困境,就不要再給本身繼續挖坑了。當你的單體應用變得沒法管理時,這是一個很好的可供參考的建議。換句話說,若是你有一個龐大的、複雜的單體應用程序,請不要經過向單體添加代碼來實現新功能。這將使你的單體變得更龐大,更難以管理。相反,你應該將新功能實現爲服務。性能
這是開始將單體應用程序遷移到微服務架構的好方法。它下降了單體的生長速度,加速了新功能的開發(由於是在全新的代碼庫中進行開發),還能快速展現採用微服務架構的價值。測試
把新的服務與單體集成代理
圖 1顯示了將新功能實現爲服務後的應用程序架構。除了新服務和單體外,該架構還包括另外兩個將服務集成到應用程序中的元素:
■ API Gateway:將對新功能的請求路由到新服務,並將遺留請求路由到單體。
■ 集成膠水代碼:將服務與單體結合。它使服務可以訪問單體所擁有的數據,並可以調用單體實現的功能。
什麼時候把新功能實現爲服務
理想狀況下,你應該在絞殺者應用程序中而不是在單體中實現每一個新功能。你將實現新功能做爲新服務或做爲現有服務的一部分。這樣你就能夠避免和單體代碼庫打交道。不幸的是,並不是每一個新功能均可以做爲服務實現。
由於微服務架構的本質是一組圍繞業務功能組織的鬆耦合服務。例如,某個功能可能過小而沒法成爲有意義的服務。例如,你可能只須要向現有類添加一些字段和方法。或者新功能可能與單體中的代碼緊耦合。若是你嘗試將此類功能實現爲服務,則一般會發現,因爲過多的進程間通訊而致使性能降低。你可能還會遇到數據一致性的問題。若是新功能沒法做爲服務實現,則解決方案一般是首先在單體中實現新功能。以後,你能夠將該功能以及其餘相關功能提取到本身的服務中。
以服務的方式實現新功能,能夠加速這些功能的開發。這是快速展現微服務架構價值的好方法。它還可以下降單體的增加速度。但最終,你須要使用另外兩種策略來分解單體。你須要經過將單體中的功能提取到服務,從而將單體中的功能遷移到絞殺者應用程序。你也能夠經過水平分割單體架構來提升開發速度。咱們來看看如何作到這一點。
2.隔離表現層與後端 縮小單體應用程序的一個策略是將表現層與業務邏輯和數據訪問層分開。典型的企業應用程序包含如下各層:
■ 表現邏輯層:它由處理 HTTP 請求的模塊組成,並生成實現 Web UI 的 HTML 頁面。在具備複雜用戶界面的應用程序中,表現層一般包含大量代碼。
■ 業務邏輯層:由實現業務規則的模塊組成,這些模塊在企業應用程序中可能很複雜。
■ 數據訪問邏輯層:包含訪問基礎設施服務(如數據庫和消息代理)的模塊。 表現邏輯層與業務和數據訪問邏輯層之間一般存在清晰的邊界。業務層具備粗粒度 API,由一個或多個封裝業務邏輯的門面(Facade)組成。這個 API 是一個天然的接縫,你能夠沿着它將單體分紅兩個較小的應用程序,如圖 2 所示。
以這種方式拆分單體應用有兩個主要好處。它使你可以彼此獨立地開發、部署和擴展這兩個應用程序。特別是,它容許表現層開發人員快速迭代用戶界面並輕鬆執行A/B測試,而無須部署後端。這種方法的另外一個好處是它公開了業務邏輯的一組遠程API,能夠被稍後開發的微服務調用。
但這種策略只是部分解決方案。極可能至少有一個或兩個最終的應用程序仍然是一個難以管理的單體。你須要使用第三種策略將單體替換爲服務。
3.提取業務能力到服務中 將新功能實現爲服務,並從後端拆分出前端Web應用程序並不會讓你抵達勝利的彼岸。你仍將最終在單體代碼中進行大量開發。若是你但願顯著改進應用程序的架構並提升開發速度,則須要經過逐步將業務功能從單體遷移到服務來拆分單體應用。當你使用此策略時,隨着時間推移,服務實現的業務功能數量會增長,而單體會逐漸縮小。
你想要提取到服務中的功能是對單體應用自上而下的一個「垂直切片」。該切片包含如下內容:
■ 實現API端點的入站適配器。 ■ 領域邏輯。 ■ 出站適配器,例如數據庫訪問邏輯。 ■ 單體的數據庫模式。
如圖 3 所示,此代碼從單體中提取並移至獨立服務中。API Gateway 將調用提取的業務功能的請求路由到該服務,並將其餘請求路由到單體。單體和服務經過集成膠水代碼進行協做。集成膠水由服務中的適配器和使用一個或多個進程間通訊機制的單體組成。
提取服務一般很耗時,尤爲是當單體的代碼庫很混亂時。所以,你須要仔細考慮要提取的服務。應當重點關注重構那些可以提供不少價值的應用程序部分。在提取服務以前,問問本身這樣作的好處是什麼。
例如,提取一項實現對業務相當重要且不斷髮展的功能的服務是值得的。若是沒有太多的好處,那麼在提取服務方面投入精力是沒有價值的。在本節的後面部分,我將介紹一些用於肯定服務提取範圍和時間的策略。但首先讓咱們更詳細地瞭解一下在提取服務時將面臨的一些挑戰以及解決這些挑戰的方法。
提取服務時會遇到如下這些挑戰:
■ 拆解領域模型。 ■ 重構數據庫。
拆解領域模型
爲了提取服務,你須要從單體的領域模型中提取服務相關的領域模型。你須要進行大動做來拆分領域模型。你將遇到的一個挑戰是消除跨越服務邊界的對象引用。保留在單體中的類可能會引用已移動到服務的類,反之亦然。例如,想象一下,如圖 4 所示,你提取了Order Service,其Order類引用了單體的Restaurant類。由於服務實例一般是一個進程,因此讓對象引用跨越服務邊界是沒有意義的。你須要消除這種類型的對象引用。
提取服務一般比將整個類移動到服務中的工做量要大得多。拆分領域模型面臨的更大挑戰是提取嵌入在具備其餘職責的類中的功能。這個問題常常出如今具備過多職責的上帝類(God Class)中。例如,Order 類是FTGO應用程序中的上帝類之一。它實現了多種業務功能,包括訂單管理、送餐管理等。Delivery 實體會實現以前與Order類中的其餘功能捆綁在一塊兒的送餐管理功能。
重構數據庫
拆分領域模型不只僅涉及更改代碼。領域模型中的許多類都是在數據庫中持久化保存的。它們的字段映射到具體的數據庫模式。所以,當你從單體中提取服務時,你也會移動數據。你須要將表從單體的數據庫移動到服務的數據庫。
此外,拆分實體時,須要拆分相應的數據庫表並將新表移動到服務中。例如,在將送餐管理提取到服務中時,你須要拆分Order實體並提取出一個Delivery實體。在數據庫級別,你要拆分ORDERS表並定義新的DELIVERY表。而後,將DELIVERY表移動到該服務。
複製數據以免更普遍的更改
如上所述,提取服務須要你對單體的領域模型作出更改。例如,使用主鍵和拆分類替換對象引用。這些類型的更改可能會影響代碼庫,並要求你對單體各個受影響的部分進行普遍的更改。例如,若是拆分Order實體並提取Delivery實體,則必須更改代碼中引用被移動字段而受影響的每一個部分。進行這些改變可能會很是耗時,而且可能成爲打破單體的巨大障礙。
延遲並可能避免進行這些昂貴更改的一種好方法是使用相似於《數據庫重構》一書中描述的方法。重構數據庫的一個主要障礙是更改該數據庫的全部客戶端以使用新模式。本書中提出的解決方案是在過渡期內保留原模式,並使用觸發器在原模式和新模式間同步。而後,你能夠將客戶端從舊模式遷移到新模式。
從單體中提取服務時,咱們可使用相似的方法。例如,在提取Delivery實體時,咱們將Order實體在過渡期內大部分保持不變。如圖6所示,咱們將與交付相關的字段設置爲只讀,並經過將數據從Delivery Service複製回單體來使其保持最新。所以,咱們只須要在單體的代碼中找到更新這些字段的位置,並更改它們爲調用新的Delivery Service便可。
肯定提取何種服務以及什麼時候提取
正如我所提到的,拆解單體是耗時的。它分散了實施新功能的人力資源。所以,你必須仔細肯定提取服務的順序。你須要專一於提取可以帶來最大收益的服務。更重要的是,你但願不斷向業務部門展現遷移到微服務架構的價值。
在任何旅程中,瞭解你要去的地方相當重要。開始遷移到微服務的好方法是使用時間框架來定義工做。你應該花費很短的時間,例如幾周,集思廣益討論理想架構並定義一組服務。這將爲你提供一個目標。可是,重要的是要記住,這種架構並不是一成不變。當你分解單體並得到經驗後,你應該應用你所得到的經驗對重構計劃及時作出調整。
一旦肯定了目標,下一步就是開始拆分單體結構。可使用幾種不一樣的策略來肯定提取服務的順序。
一種策略是有效地凍結單體架構的開發並按需提取服務。你能夠提取必要的服務並進行更改,而不是在單體中實現功能或修復錯誤。這種方法的一個好處是它會迫使你打破單體。一個弊端是服務的提取是由短時間需求而不是長期需求驅動的。例如,即便你對系統中相對穩定的部分進行了少許更改,也須要你提取服務。所以,你作的大量工做可能只能換來較小的收益。
另外一種策略是更有計劃的方法,你能夠根據提取應用程序模塊得到的預期收益,對應用程序的模塊進行排名。提取服務有益的緣由有如下幾點:
■ 加速開發:若是你的應用程序的路線圖代表應用程序的特定部分將在明年進行大量開發,那麼將其轉換爲服務可加速開發。
■ 解決性能、可擴展性或可靠性問題:若是應用程序的特定部分存在性能、可擴展性問題或不可靠,那麼將其轉換爲服務是有價值的。
■ 容許提取其餘一些服務:因爲模塊之間的依賴關係,有時提取一個服務會簡化另外一個服務的提取。 你可使用這些條件將重構任務添加到應用程序的「待辦事項」中,並按預期收益排名。這種方法的好處在於它更具戰略性,而且更符合業務需求。在作 Sprint 的計劃時,你能夠肯定實現功能或提取服務哪一個更有價值。
喜歡能夠+關注+轉發+喜歡哦