如何從複雜單體應用快速遷移到微服務?

想必你已知道了微服務及其工做原理,如今是時候探討如向微服務轉變這個關鍵話題了。數據庫

爲何要向微服務轉變編程

總體式(monolithic)應用程序很龐大(代碼行數方面)、很複雜(功能依賴和數據等方面),爲跨地區的成千上萬用戶提供服務,須要多個開發人員和 IT 工程師。瀏覽器

總體式應用程序可能相似下圖:安全

圖 1:總體式應用程序的基本結構服務器

有時,即便具備全部這些特色,應用程序最初也可能順暢運行,可能在應用程序可擴展性或性能方面不會遇到挑戰。但用着用着會出現問題,問題因應用程序而異。架構

好比對於雲或 Web 應用程序而言,因爲更多用戶使用服務,你可能遇到可擴展性問題,或者因爲更長的構建時間和迴歸測試,按期發佈新的更新可能變得成本高、難度大。併發

如圖 2 所示,總體式應用程序的用戶或開發人員可能遇到右邊列出的一個或多個問題。負載均衡

圖 2:總體式應用程序的潛在問題dom

這時遷移到微服務可能聽起來不只僅是時髦的想法,更像是大救星。應用程序的遷移會相似圖 3 所示:異步

圖 3:從總體式應用程序向微服務轉變

那麼,如何進行這種轉變?有兩種可能的場景:

  • 建立全新的應用程序。
  • 轉換或遷移已經存在的總體式應用程序。

後一種場景的可能性大得多,但不管目前的狀況如何,都有必要了解這兩種場景的前因後果。

使用微服務建立新應用程序

我尚未看到不少從頭開始構建基於微服務的應用程序的真實場景。一般,應用程序已部署到位,我搞過的大多數應用程序更可能是從總體式架構向微服務架構轉變。

在這種狀況下,架構師和開發人員的意圖一直是重用一些現有的實現。但因爲技能在市場上很是廣泛、一些成功的實現發佈,咱們會看到從頭開始構建基於微服務的應用程序的更多例子,所以固然有必要探究這種場景。

假設你已摸清了全部需求,準備好處理要構建的應用程序的架構設計。你在入手時須要考慮許多常見的最佳實踐,這些實踐在下面各節中有介紹。

組織準備

你要問本身的第一個問題是,貴組織是否準備好向微服務轉變。

這意味着貴組織的各個部門如今須要從如下方面對軟件的構建和發佈進行不一樣的思考:

  • 團隊結構。總體式應用程序團隊(若是有的話)須要分解成幾個知道微服務最佳實踐或受到培訓的小規模高績效團隊。

如圖 3 所示,新系統將包含一組獨立服務,每一個服務負責提供特定服務。這是微服務模式的一大優點:減小了通訊開銷,包括多場不間斷會議。

團隊應按照所要解決的業務問題或領域加以組織。而後,溝通變爲敲定要遵循的一套標準/協議,那樣這些微服務就能彼此協做。

  • 每一個團隊必須準備獨立於其餘團隊運做。它們的規模應至關於標準的 Scrum 團隊,不然溝通會再次成爲問題。執行是關鍵,每一個團隊都應可以知足不斷變化的業務要求。
  • 工具和培訓。一個關鍵要求是組織準備投入於新工具和人員培訓的狀況。在大多數狀況下,現有的工具和流程須要停用,採用一套新的。

這須要投入大量資本,致力於招聘擁有新技能的人員,並從新培訓現有工做人員。從長遠來看,若是採用微服務的決定是正確的,組織會看到成本節省,於是收回投入。

基於服務的方法

與總體式應用程序不一樣,如果微服務,你須要採用自我維持的基於服務的方法。

應用程序比如是一組鬆散耦合的服務,這些服務相互通訊以提供完整的應用程序功能。

應將每項服務視爲有其生命週期的獨立服務,可由獨立團隊開發和維護。這些團隊要從各類技術中進行選擇,包括最適合其服務要求的語言或數據庫。

好比針對電子商務站點,團隊要編寫一個徹底獨立的使用內存數據庫的服務,好比購物車微服務,以及使用關係數據庫的另外一項服務,好比訂購微服務。

實際應用程序可能將微服務用於基本功能,好比身份驗證、賬戶、用戶註冊和通知,業務邏輯封裝在 API 網關中,API 網關基於客戶端和外部請求調用這些微服務。

提醒一下:微服務多是一個開發人員實現的小服務,也多是須要多個開發人員的複雜服務。就微服務而言,大小可有可無;它徹底依賴服務要提供的一項功能。

此時必須考慮的其餘方面是擴展、性能和安全。擴展要求可能不同,應在每一個微服務層面根據須要來提供。應在全部層面考慮安全,包括靜態數據、進程間通訊和傳輸中數據等。

進程間(服務到服務)通訊

必須考慮的關鍵方面是安全和通訊協議。異步通訊是最佳選擇,由於它可確保全部請求正常運行,不會長時間佔用資源。

使用 RabbitMQ 等消息總線可能有利於這種通訊。它很簡單,能夠擴展到每秒數十萬條消息。

爲防止消息傳遞系統在發生故障後成爲單一故障點,必須正確設計消息傳遞總線以實現高可用性。其餘選項包括另外一種輕量級消息傳遞平臺 ActiveMQ。

安全是該階段的關鍵。除了選擇正確的通訊協議外,可以使用 AppDynamics 之類的行業標準工具來監控和衡量進程間通訊。須自動向安全團隊報告任何異常狀況。

如有數千個微服務,處理一切確實變得複雜起來。後面會解釋如何藉助發現服務和 API 網關解決此類問題。

技術選擇

向微服務轉變的最大優點是讓你能夠選擇。每一個團隊能夠獨立選擇最適合特定微服務的語言、技術和數據庫等。

若採用總體式方法,團隊一般沒有這樣的靈活性,所以確保你沒有忽視並錯過該機會。

即便團隊在處理多個微服務,也要將每一個微服務視爲獨立的服務並進行分析。

爲每一個微服務選擇技術時,必須牢記可擴展性、部署、構建時間、集成和插件可操做性等。

若是是數據較少但訪問較快的微服務,內存數據庫可能最合適,而其餘微服務可能使用一樣的關係數據庫或 NoSQL 數據庫。

實現

實現是關鍵階段,這時候全部培訓和最佳實踐知識派得上用場。

要記住的幾個關鍵方面包括:

  • 獨立性。每一個微服務都應高度自主,有本身的生命週期並以此進行處理。它的開發和維護不須要依賴其餘微服務。
  • 源代碼控制。必須部署適當的版本控制系統,每一個微服務要遵循標準。統一代碼庫也頗有幫助,由於它能夠確保全部團隊使用相同的源代碼控制。

它有助於代碼審查等各個方面,便於在一個地方訪問全部代碼。長遠來看,有必要對全部服務實行一樣的源代碼控制。

  • 環境。全部不一樣的環境(如開發、測試、模擬和生產等階段)必須獲得適當的保護和自動化。這裏的自動化包括構建過程。

那樣,能夠根據須要集成代碼,大多天天進行。有幾種工具可用,不過 Jenkins 普遍使用。Jenkins 是一種開源工具,有助於軟件構建和發佈過程實現自動化,包括持續集成和持續交付(CI/CD)。

  • 故障安全。軟件故障不可避免。須在微服務開發中解決好下游服務的故障處理問題。其餘服務的故障必須是隱形的,好讓用戶看不到故障。

這包括管理服務響應時間(超時)、處理下游服務的 API 更改以及限制自動重試次數。

使用微服務時,別懼怕經過使用複製粘貼來重用代碼,但這麼作要有限制。

這可能致使代碼重複,但這賽過使用最終耦合服務的共享代碼。微服務中,你須要的是去耦,不是緊耦合。

好比說,你將編寫代碼以使用服務的輸出響應。每次從任何客戶端調用相同的服務時,你能夠複製此代碼。

重用代碼的另外一種方法是建立公共庫。多個客戶端可使用相同的庫,但隨後每一個客戶端應負責維護其庫。

若是你建立太多的庫,每一個客戶端維護不一樣的版本,有時變得困難重重。這種狀況下,你要包含相同庫的多個版本,因爲向後兼容性和相似問題,構建過程可能變得困難。

只要你能夠控制客戶端的庫和版本數量,並對它們實行嚴格的流程,能夠採用任何一種方式,這就看你的須要了。這確定有助於避免大量的代碼重複。

鑑於微服務數量龐大,調試問題可能會變得困難,所以你須要在此階段進行某種檢測。

最佳實踐之一是使用惟一的請求 ID 標記每一個請求,並記錄每一個請求。這個惟一的 ID 標識始發請求,應由每一個服務傳遞給任何下游請求。

看到問題後,你能夠經過日誌清楚地回溯並找出有問題的服務。若是你創建集中式日誌記錄系統,該解決方案最有效。

全部服務都應以標準化格式將全部消息記錄到此共享系統,以便團隊能夠根據須要從一個地方(從基礎設施到應用程序)重放事件。用於集中式日誌的共享庫值得研究。

市面上有幾種很理想的日誌管理和聚合工具,好比 ELK(Elasticsearch、Logstas和Kibana)以及 Splunk。

部署

自動化是部署過程當中的關鍵。沒有它,微服務模式幾乎不可能成功。可能有成百上千的微服務,對於敏捷交付而言,自動化必不可少。

設想部署數千個微服務並維護。其中一個微服務發生故障後會發生什麼?怎麼知道哪臺機器有足夠的資源來運行你的微服務?

若沒有落實自動化,應對這種狀況變得很是複雜。Kubernetes 和 Docker Swarm 等各類工具可用於自動化部署過程。

操做

整個過程的操做部分也須要自動化。這裏談論的一樣是成百上千的微服務,組織能力須要足夠成熟才能處理這種複雜程度。

你須要一個支持系統,包括如下方面:

  • 從基礎設施、應用程序 API 到最後一英里性能,所有都要加以監控,並實施具備適當閾值的自動警報。考慮構建問題出現後彈出數據和警報的實時儀表板。
  • 按需可擴展性。若使用微服務,擴展是最簡單的任務。配置你想要擴展的微服務的另外一個實例,將它放在現有的負載均衡器後面就行。

但在規模化環境中,這也須要實現自動化。只需設置一個整數值,告訴想要爲特定微服務運行的實例數量。

  • API 公開。在大多數狀況下應該對外公開 API 以供外部用戶使用。最好使用邊緣服務器來完成這項任務,該服務器能夠處理全部外部請求。

它可使用 API 網關和發現服務來完成任務,你能夠針對每種設備類型(好比移動設備或瀏覽器)或用例使用一臺邊緣服務器。Netflix 開發的一款開源應用軟件 Zuul 可用於此功能及其餘功能。

  • 斷路器。向故障服務發送請求毫無心義。所以能夠構建斷路器(circuit breaker),跟蹤針對每一個服務的每一個請求的成功或故障。若出現多個故障,應阻止對該特定服務的全部請求一段指定的時間(即斷開電路)。

指定時間事後,應進行另外一次嘗試,依此類推。一旦響應成功,從新鏈接電路。這應該在服務實例層面進行。Netflix 的 Hystrix 提供了開源斷路器實現。

將總體式應用程序遷移到微服務

雖然構建基於微服務的新應用程序的大多數最佳實踐也適用於遷離現有的總體式應用程序,但若是遵循另一些準則,可以使遷移更簡單、更高效。

雖然將整個總體式應用程序轉換成徹底基於微服務的應用程序聽起來可能很正確,但將每項功能轉換成微服務可能並不高效,在一些狀況下可能成本很高。

畢竟,你到頭來要從頭開始編寫應用程序。正確的遷移方式可能須要逐步進行,如圖 4 所示:

圖 4:基本的遷移步驟,從總體式應用程序到微服務

下一個問題是:目前的總體式應用程序從何處入手?若是應用程序確實很舊,進行分解很耗時、難度大,從頭開始可能更好。

在能夠快速禁用部分代碼、技術架構並不徹底過期的其餘狀況下,最好先將組件從新構建爲微服務,並換掉舊代碼。

微服務標準

那麼問題變成了哪些組件應該先遷移或甚至要不要遷移。這讓我想到了所謂的「微服務標準」,這概述了選擇應遷移到微服務的功能並肯定優先級的可行方法之一。

它們是你制定的一套規則,根據組織當時的要求,決定將現有總體式應用程序的組件轉換成微服務是否適合。

這時機在這裏很重要,由於組織的要求可能不斷變化,你可能要回過頭來,將更多組件轉換成微服務。

換句話說,因爲要求變化,總體式應用程序的額外組件可能適合轉換。如下是轉換過程當中被視爲微服務標準的幾個最佳實踐:

①你須要肯定哪些功能頻繁使用

先將頻繁使用的服務或應用程序功能轉換成微服務。記住:微服務只執行一個明肯定義的服務。牢記這個原則,相應地劃分應用程序。

②可能存在性能不佳的組件,其餘替代方案隨時可用

可能有開源插件,或者你可能想從頭開始構建服務。應牢記的要點之一是微服務的邊界。

只要你設計的微服務只作一件事,就很好。肯定邊界經常很難,你會發現實踐一下會更容易。

查看微服務邊界的另外一種方法是,應該幾周內就能重寫整個微服務,而不是花幾個月來重寫服務。

③更好的技術替代方案或多語言編程

針對特定領域的語言可用於幫助解決問題域(problem domain)。這尤爲適用於過去你收到許多改進請求,預計未來會繼續如此的組件。

若是你不只認爲可使用市面上的新語言或功能簡化這種組件的實現,未來的維護和更新還會變得更容易,如今正是應對這種變化的時候。

在其餘狀況下,你可能發現另外一種語言提供的併發抽象比目前使用的語言更容易。

能夠將新語言用於特定的微服務,而應用程序的其他部分仍然使用不一樣的語言。

一樣,你可能但願一些微服務很是快,可能決定用 C 語言編寫以得到最大效益,而不是用另外一種高級語言編寫。說究竟是要利用這種靈活性。

④存儲替代方案或多語言持久性

大數據大行其道,若是使用 NoSQL 數據庫而不是關係數據庫,應用程序的一些組件可能會提供價值。

若是應用程序中的任何此類組件可得益於該替代方案,可能正是改用 NoSQL 的時候。

這些是你應該爲總體式應用程序中的每一個服務或功能而考慮的關鍵方面,你須要先注重這幾項的轉換。一旦你從高優先級的部分得到了價值,隨後能夠運用其餘規則。

⑤修改請求

任何軟件生命週期中要跟蹤的一個重要方面是新的改進請求或更改。因爲構建和部署時間,更改請求數量更多的功能可能適用於微服務。

分離這類服務能夠縮短構建和部署時間,由於你沒必要構建整個應用程序,只需更改微服務,這還能夠爲應用程序的其他部分提升可用性時間。

⑥應用程序的某些部分老是增長部署的複雜性

在總體式應用程序中,即便某項功能未加變更,你仍得完成整個構建和部署過程。

若是存在這種狀況,劃分這些組件並用微服務取代大有助益,這樣能夠爲總體式應用程序的其他部分縮短總的部署時間。

⑦輔助服務

在大多數應用程序中,核心或主要的服務依賴一些輔助服務。沒有這類輔助功能,可能會影響核心服務的可用性。

好比在求助臺應用程序中,工單依賴產品目錄服務。若是產品目錄服務不可用,用戶沒法提交工單。

若是存在這種狀況,應將輔助服務轉換成微服務,並確保高可用性,以便它們能夠更好地服務於核心服務。(這些又叫斷路器服務。)

視應用程序而定,這些標準可能須要將大多數服務轉換成微服務,目的是簡化轉換過程,那樣你能夠肯定優先級,併爲遷移到基於微服務的架構制定路線圖。

爲服務從新設計架構

一旦肯定了要遷移的轉換成微服務的功能,能夠遵循前面所述的最佳實踐,開始爲已選擇的服務從新設計架構。

如下是須要牢記的幾個方面:

  • 微服務定義。爲每一個功能定義適當的微服務,應包括通訊機制(API)和技術定義等。

考慮現有功能使用的數據,或針對微服務相應地建立和規劃數據策略。若是該功能在 Oracle 之類的密集數據庫上,遷移到 MySQL 是否有意義?

肯定你將如何管理數據關係。最後,將每一個微服務做爲單獨的應用程序來運行。

  • 重構代碼。若是你未改變編程語言,能夠重用一些代碼。考慮存儲/數據庫層:共享 vs 專用,內存中 vs 外部。

目的在於除非須要,不然不添加新功能,而是從新打包現有代碼並公開所需的 API。

  • 開始編碼以前,肯定源代碼控制和版本控制機制,並確保遵循這些標準。每一個微服務都是單獨的一項,做爲單獨的應用程序來部署。
  • 數據遷移。若是你決定建立新數據庫,還要遷移舊數據。這一般經過編寫簡單的 SQL 腳原本完成,具體取決於你的源代碼和目的地。
  • 總體式代碼。最初將現有代碼留在總體式應用程序中,以防萬一要回滾。你能夠更新其他代碼以使用新的微服務,或者劃分應用程序流量(若是可能),同時使用總體式版本和微服務版本。

這讓你有機會測試和關注性能。一旦有信心,你能夠將全部流量遷移到微服務,禁用或刪除舊代碼。

  • 獨立地構建、部署和管理。要獨立構建和部署每一個微服務。推出新版本的微服務時,能夠再次劃分舊版本和新版本之間的流量。

這意味着你可能在生產環境中運行相同微服務的兩個或更多版本。一些用戶流量能夠路由到新的微服務版本,確保服務正常運行、性能良好。

若是新版本未在最佳狀態下運行,很容易將全部流量回滾到先前版本,並將新版本退回給開發團隊。這裏的關鍵是創建可重複的自動化部署流程,竭力實現持續交付。

  • 刪除舊代碼。只有在確認一切已正確遷移並按預期運行後,才能刪除臨時代碼,並從舊存儲位置刪除數據。務必在此過程當中作好備份。

微服務的混合方法

開發人員編寫全新的應用程序時,能夠直接遵循微服務架構原則和藍圖來構建應用軟件。開發人員有時遵循微服務和總體式應用程序的混合方法。

在這種狀況下,他們能夠將應用程序的部分組件開發成微服務,其他部分基於某些標準遵循標準的 SOA/MVC 實踐。

其想法是,並不是應用程序的全部組件均可以轉換成微服務。微服務提供了很大的靈活性,但這種靈活性要付出一些代價。

混合方法旨在兼顧靈活性和成本這兩方面,之後能夠根據須要從總體式應用程序獲取組件、轉換成微服務。關鍵是在這個轉變期間牢記這兩種方法以及微服務標準。

感興趣的能夠本身來個人Java架構羣,能夠獲取免費的學習資料,羣號:855801563對Java技術,架構技術感興趣的同窗,歡迎加羣,一塊兒學習,相互討論。

相關文章
相關標籤/搜索