7-從單體應用遷移到微服務

7-從單體應用遷移到微服務

本文出自Nginx官網,是微服務介紹系列文章的第七篇,也是最後一篇。原文地址:https://www.nginx.com/blog/refactoring-a-monolith-into-microservices/

1.介紹

這是微服務系列文章的最後一篇,在第一篇文章中我們比較了微服務架構應用和單體應用的差異,討論了微服務架構的優點與缺點;隨後又討論了使用API網關、進程間通信、服務發現、處理分佈式數據管理、微服務部署等內容;在本篇文章中我們討論將單體應用遷移到微服務的策略。

希望這個系列能讓你瞭解微服務的架構、微服務的好處和缺點、什麼時候選用微服務;微服務架構在很多情況下是更好的選擇,可你很可能正工作於巨大、複雜的單體應用,每天的開發、維護、部署工作,效率低下、充滿痛苦;而微服務看起來像是遙不可及的美夢。所幸,從單體應用遷移到微服務架構,有可以遵循的策略,這篇文章中,我們就討論這些策略。

2.遷移工作概述

將單體應用遷移到微服務的過程,是應用現代化的過程,這是全世界開發人員十幾年來一直努力在做的事情,有一些好的思想可以借鑑。

不推薦使用“大爆炸”式的將原單體應用推倒重寫的策略,“大爆炸”聽起來很美好,實際上風險很大,最終很可能失敗。就像Martin Fowler所說的,“大爆炸式的推倒重來,唯一能保證的就是大爆炸式的災難後果”。

應該採用漸進式重構的方式進行遷移,逐步構建由微服務構成的新應用,讓它跟單體應用協同工作;慢慢的,單體應用實現的功能越來越少,直至完全消失或者變成另外一個微服務。這種策略類似於向在高速公路上以70英里時速行駛的汽車提供服務,具有挑戰性,但風險遠低於大爆炸式的重寫。

Martin Fowler將這種應用現代化的策略稱爲“Strangler Application”,這個名字來自於熱帶雨林的一種植物“strangler vine”。strangler vine沿着樹幹向上生長,以獲取陽光;最後大樹死去,只剩下樹形的strangler vine。應用現代化的過程類似於這種模式,我們在單體應用的周圍構建由微服務組成的新應用,直至原有應用死去。接下來,我們討論不同的遷移策略。

3.策略1-停止挖坑

深坑法則告訴我們,掉進深坑後第一件要做的事情,就是別再繼續挖坑。當單體應用已經大到不可維護時,這個建議非常有價值;換句話說,別讓單體應用更大了。這意味着不要向單體應用添加新的代碼,應該將新代碼放到獨立的微服務中,下圖展示了這種結構:


在上圖中,除了遺留的單體應用和新構建的服務之外,還有兩部分內容:第一部分是處理HTTP請求的請求路由器,有點兒類似於之前描述的API網關;路由器將新服務的請求轉發給服務,將老請求轉發給遺留的單體應用。另外一部分是將服務和單體應用組合在一起的膠水代碼,通常新服務不會獨立存在,它依賴單體應用的數據。膠水代碼可能存在於微服務、單體應用,或者兩者都有,它負責組合數據;微服務使用膠水代碼讀寫單體應用擁有的數據。

服務使用三種策略訪問單體應用的數據:使用單體應用的API遠程調用;直接訪問單體應用的數據庫;維護單體應用數據的一份拷貝。

膠水代碼也被稱作反腐敗層,它防止服務自身領域模型受到單體應用領域模型的污染;膠水代碼在兩個不同的模型之間進行翻譯。如果你想從單體應用的地獄中走出,膠水代碼是至關重要的工作。

將新功能構建成服務有很多好處:它阻止單體應用變得更大;服務可以獨立於單體應用進行開發、部署和擴展。你能體驗到使用微服務架構創建的每一個服務帶來的好處。

停止挖坑的做法沒有讓事情變得更壞,但它沒有解決原有單體應用的任何問題,爲了解決問題,需要拆分單體應用,接下來將描述單體應用拆分的策略。

4.策略2-拆分前端和後端

這種策略通過將表示層從業務邏輯層和數據層中拆分,實現單體應用規模的縮減。通常企業應用至少包括三層:

表示層:處理HTTP請求,實現REST風格API或者Web UI。如果應用有比較複雜的用戶界面,表示層一般會有很多代碼。

業務邏輯層:是應用的核心,實現業務邏輯。

數據訪問層:訪問類似數據庫和消息中間件等基礎實施。

一般情況下,表示層和另外兩層有比較明顯的區分。業務邏輯層會使用一個或者幾個façade模式封裝業務邏輯,對外提供粗粒度的API;這些API就是拆分應用的天然接縫。你可以沿着API將應用拆分,一個應用包含表示層代碼,另外一個包含業務邏輯和數據訪問代碼,表示層應用遠程調用業務邏輯應用提供的服務。以下圖示展示了拆分前和拆分後的架構:


通過上述策略拆分單體應用有兩個好處:兩個應用可以獨立的開發、部署、擴展,允許開發人員在用戶界面上快速迭代並輕鬆執行A/B測試;應用暴露了遠程訪問的接口,可以供後續開發的其他服務調用。

當然,經過上述拆分之後仍然沒有從根本上解決問題,原應用很可能變成兩個同樣無法維護的單體應用,那就需要使用第三個策略進行進一步拆分。

5.策略3-服務抽取

這個策略是將單體應用的模塊抽取成服務,每抽取出一個服務,單體應用就縮減一點兒;當你抽取出足夠多的服務時,單體應用就會完全消失或者轉化爲另外一個服務,問題就會完全解決。

確定服務抽取的優先級

大型應用通常有數十個甚至數百個模塊,所有模塊都可以進行服務抽取,如何確定服務抽取的優先級是個有挑戰的工作。一個好的辦法是從容易進行服務抽取的模塊開始,這能讓你積累微服務構建和服務抽取的經驗,接下來應該抽取那些能帶來最大好處的模塊。

將應用模塊轉化成服務是耗費時間的工作,應該根據轉換工作能帶來的好處確定優先級。通常來說,抽取經常變化的模塊能帶來較大的好處,一旦你將模塊抽取成服務,它就能獨立的開發和部署,從而提高開發和部署效率。

將與其他模塊資源需求明顯不同的模塊抽取成服務,也能帶來很大好處。比如,將使用內存數據庫的模塊抽取成服務,它就能部署在有大內存的主機上;同樣,將需要大量計算資源的模塊抽取成服務,就能將它部署到有優異計算性能的主機上;通過將不同資源需求的模塊抽取成服務,可以讓應用更容易擴展。

當確定要抽取哪些模塊時,查找粗粒度的模塊邊界很有用,能簡化服務抽取工作。比如,如果有個模塊的邊界是僅通過異步消息跟其他模塊通信,那將該模塊抽取成服務就相對簡單。

如何進行服務抽取

第一步是抽取出候選模塊和單體應用的接口,既然單體應用和候選模塊都需要對方的數據,這個接口很可能是雙向依賴的;由於候選模塊和單體應用之間複雜的依賴關係,且往往存在細粒度的交互,抽取接口通常是困難的。重構使用領域模型實現的業務邏輯模塊更加困難,因爲在領域模型的不同類之間往往存在大量複雜的交互,需要改動很多代碼來減少相互依賴。

一旦實現了粗粒度的接口,候選模塊就變成了獨立服務,候選模塊和單體應用之間採用進程間通信機制。以下圖示展示了重構前、重構中、重構後的架構:


在這個例子中,計劃將Z模塊抽取成服務,它調用Y模塊,又被X模塊調用。第一步是定義一對接口,一個接口是入站接口,X模塊用來調用Z模塊;另外一個接口是出站接口,Z模塊用來調用Y模塊。

第二步是將Z模塊轉化爲獨立的服務:使用進程間通信機制實現出站、入站兩個接口;組合微服務基礎框架和Z模塊,實現服務發現等功能。

一旦你抽取出一個模塊,你就可以開發、部署、擴展獨立於應用和其他服務的另一個服務;你甚至可以重寫服務;在這種情況下,例子中的兩個接口起到了防止腐敗的作用,它們集成候選模塊和單體應用,並在兩者的領域模型之間進行翻譯。每抽取出一個服務,應用就朝微服務邁出一步,慢慢的,微服務會逐漸壯大,單體應用會逐漸縮減。

6.總結

將單體應用遷移到微服務是應用現代化的過程,不要從頭重寫應用,應該逐步重構。有三種策略可以使用:將新功能構建成服務,將表示層構建成服務,將已有模塊抽取成服務,隨着微服務數量的不斷增加,開發團隊的敏捷性和開發效率會不斷提升。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章