譯者 | 朱鋼
策劃 | 云昭
微服務架構設計中,如何拆分單體是一件非常重要且令架構師十分頭疼的問題。在這篇文章中,會展現一些關于如何準備和執行單體應用程序拆分的思路和步驟說明。
概述
單體拆分必須追求的一些目標:不僅僅是拆分,而是通過拆分獲得一些收益。 如果考慮到拆分的成本和效果,可能其他一些方法(例如應用程序擴展或數據庫硬件更新)可能更可取。
要實現的另一個好處的例子可能只是應用程序現代化。 然而這里是一個或多或少正式的單體拆分方式,它試圖考慮拆分的原因和目標。 當然這不是教條,你可以找到幾種拆分的方法。 此遷移路線圖旨在將單體應用程序遷移到微服務以此獲得微服務的優勢,而不會(或極小)造成應用程序不可用。
明確基準線
由于應用程序支持性,單體應用程序可能看起來非常混亂,但這是意料之中的。 最好的情況是,期待一個帶有大量調整的大型耦合分層應用程序。 無論如何,第一步是你需要收集有關當前應用程序的所有信息:需求、用例、ASR、組件、部署圖等。
理解應用程序(整體)
在大多數情況下,應用程序缺少文檔,這些文檔可能描述了基準線,那么你至少需要收集以下信息:
- 應用程序的功能模塊、它們之間的關系和外部環境;
- API和服務接口描述;
- 帶有模塊描述的組件圖就足夠了。這是因為在大多數情況下,拆分將按照功能或換句話說應用程序功能執行;
- 部署圖,顯示物理硬件以及系統和軟件配置。這是因為一些NRF可能會受到部署的影響(例如,故障隔離的可擴展性)。
理解數據
為什么數據圖表和潛在數據映射很重要? 在大多數情況下,微服務將與底層數據和數據庫一起抽取。雜亂無章的數據可能會影響此類 NRF 的性能。 在任何情況下,它都有助于發現潛在的問題,例如數據缺乏或過多、數據混亂以及使用某些微服務進行數據抽取的邊界。
理解上下文
在某些情況下,需要抽取的功能可能已經作為單獨的外部服務存在了。不需要抽取一些業務能力,只需要復用一些已經存在的外部服務。像 TTM 這樣的微服務優勢可以用更少的努力來實現。
理解用戶
這雖然與上下文重疊,但顯示了用戶/外部系統是如何使用應用程序的,以便了解使用什么功能以及如何使用,也許應用程序的某些部分已過時且未使用。
其余關于應用程序的信息將用于做出決策并顯示拆分的必要性,如何拆分以及抽取什么。
為什么要拆分單體?
可能很難回答為什么我們需要將單體應用拆分為微服務,答案可以更加面向業務或更加面向技術。在大多數情況下,企業是看不到將單體架構重構或拆分為微服務的任何好處。
業務驅動
乍一看,可以找到以下先決條件,但列表可能會擴大。 無論如何,業務驅動都是與增加收入和減少損失有關的,因此新的項目都只會從這兩個問題中衍生出來。
- 縮短上市時間
- 降低 TCO:僅擴展必要的部分
- 減少損失:彈性、高可用性和容錯解決方案、測試覆蓋率
- 提供更好的用戶體驗
此外,一些奇怪的卻動力可能默默地出現,但不會展現出來:它很主流并且也拆散了整體。
技術驅動
技術驅動只是擴展了業務驅動的列表,可能如下所示:
- 擺脫過時的技術并順利遷移到現代/受支持的技術
- 降低復雜性
- 提高可支持性
- 提高測試覆蓋率
- 可重用性
- 透明且可預測的變化
- 團隊重用
微服務的優勢
下面提供了微服務的好處以及它們如何滿足業務和技術需求。 當然,表格可以通過幾個額外的微服務好處進行擴展,但它們大多是派生出來的,并與表格內容重疊。
記住優勢的同時,也請不要忘記缺點。
拆分步驟
1、準備
以下是或多或少拆分單體應用的正式步驟。這些步驟可能會根據業務需求、單體狀態、CI/CD 程序等進行調整。
在建立基線時早期獲得的模塊/服務列表應圍繞業務能力進行組織,并且可能更深一層。例如欺詐檢測系統和促銷服務可能需要地址。
可以在拆分過程的中間實現期望的目標。從業務的角度來看,這可能是件好事,但從技術的角度來看,繼續使用包含多個抽取服務的單體應用程序就很糟糕。
2、重構優先
需要拆分的應用程序幾乎不可能具備良好的形態。因此可能會在單體應用中執行幾輪服務重構,一些服務可能正在重構中,而另一個正在抽取中。
3、典型的分層應用
這是服務抽取的起點,此時應用程序的主要層,如控制器、應用程序服務、DAO 等,是可觀察和可理解的。
4、服務/功能邊界和 API
DDD 可能有助于定義服務邊界,但我們需要從現有應用程序中提取業務功能,而不是對業務模型進行建模。只需開始將業務功能映射到現有的應用程序服務和域模型中以識別服務邊界即可。
在分層應用程序中,控制器可以描述要提取的 API。API 可能會被調整/擴展,但這里的重點是整個應用程序必須與服務一起工作,并且更改最少。更改與我們需要使用抽取功能(代理和外觀)的原因和方式有關。
5、創建服務facade
當定義服務邊界時需要改變與功能交互的方式,而不是處理一組代表業務功能的應用程序服務,我們需要在單體應用程序中創建一個外觀并通過facade工作。
換句話說,我們需要在提取服務之前獲得一個松散耦合的單體。
6、重構數據
使用數據庫抽取一項服務可能會影響單體應用的多個不同服務。所以只能通過 API 訪問服務數據,而不是通過數據庫。因此,由于直接訪問了數據,即使是一個服務的提取,也可能需要大量工作。
7、停止將新功能寫入單體應用程序
拆分到微服務和向解決方案中添加新功能著兩個活動并行執行,這是一種常見的情況,因此從一開始就將新功能創建為單獨的微服務是有意義的。
8、拆分
大多數情況下,所有準備步驟都應在實際拆分之前完成。應用程序應呈現為松散耦合的單體,具有細粒度的服務、良好描述的 API 和邊界以及每個服務的隔離數據(避免跨數據庫共享數據)。
9、優先提取服務
更正確的做法是不進行服務抽取,而是進行業務特征抽取。嘗試按業務能力/領域對現有應用進行重組/重構/分組,每個應用可能包含多個應用服務。
潛在的優先標準:
- 最常更改的服務:最小化對部署的影響。
- 可能被第三方服務取代的服務:只是為了讓代碼庫更輕量級。
- 需要擴展的服務:優化性能。
- 與整個單體耦合:使代碼庫更輕巧且更易于理解。
- 服務的復雜性:收集經驗、建立CI/CD流程、溝通方式等。
清單可能會不同, 可能添加諸如將底層數據庫模型從關系數據庫模型更改為 NoSQL、技術更改(編程語言)、團隊利用率等標準。
結果,可以選擇至少一項服務進行提取。
10、選擇微服務之間的通信方式
這不是一個非常復雜的步驟。但是我們不僅需要選擇協議和序列化類型,還需要考慮云提供商的限制(即廣播支持)。在大多數情況下,REST 或 gRPC 是請求-響應同步通信的首選方式。這是由于相對簡單、團隊經驗、不同工具的支持等原因。很難想象新的或抽取的微服務使用干凈的 TCP。
基于消息的通信方式由于異步性、單向(當然可以使用“reply to”機制,但會導致服務耦合)等原因,不能被視為請求-響應的替代方案。異步適用于事件源(即發即棄),也適用于消費者構建自己的世界圖景。一個例子是關于成功付款的通知將反映在訂單履行系統中以組裝交付和財務系統,可以使用 SQS、Kafka 或其他一些消息傳遞服務。
11、實施服務
使用選定的通信方法、擁有的數據庫等實現微服務。創建一個用于測試目的的服務模擬來測試單體和其他相關微服務是有意義的。
12、創建服務代理
創建具有額外責任的服務代理。在單體應用中使用服務或使用抽取服務,暗示代理可用于與刪除/抽取的微服務進行通信。
該代理將允許在單體應用中的現有服務實現和抽取的服務之間輕松切換。可以使用canary發布方法。
13、切換到微服務
經過測試,在生產環境中進行測試的canary發布僅對微服務使用和從舊代碼中清理單體應用有意義。
檢查是否達到了初始目標,并決定停止或繼續拆分過程。在繼續的情況下,你需要選擇下一個特征并執行提取。
譯者介紹
朱鋼,51CTO社區編輯,2019年CSDN博客專家20強,2020年騰訊云+社區優秀作者,10年一線開發經驗,曾參與獵頭服務網站架構設計,企業智能客服以及大型電子政務系統開發,主導某大型央企內部防泄密和電子文檔安全監控系統的建設,目前在BIM頭部企業從事招投標軟件開發。
原文標題:??Split the Monolith: What, When, How??,作者:Igor Azarny