從單體遷移到微服務的幾種模式
作者 | Abhishek Kapoor 、譯者 | 王強
策劃 | 蔡芳芳 、來源 |架構頭條
正確實現的微服務較單體應用有很多優勢。許多組織都希望將他們的單體應用程序代碼換成微服務代碼。但事實證明,遷移到微服務並非易事。你應該問的第一個問題是,你真的需要微服務嗎?單體存在的許多問題都可以使用模塊化的單體架構輕鬆解決。一旦你確定自己真的需要微服務,就必須制定一套將單體應用轉換爲微服務的計劃。本文介紹了一些模式,可以幫助你創建所需的計劃。
在我們具體討論這些拆分單體的模式之前,我們先來談談不應該做的一些事情。
不要做大爆炸重寫
大爆炸重寫(Big Bang Rewrite),顧名思義,是說我們必須在許多微服務中重寫整個單體應用的代碼,並一次性將它們都部署到生產環境中。Martin Fowler 說過的一句話非常正確:
大爆炸重寫唯一能保證的就是大爆炸!
大爆炸重寫都是很危險的。大爆炸重寫需要漫長的開發時間,因爲你必須對單體應用程序中的所有內容重新編碼。此外,在微服務架構的開發過程中,你必須凍結單體應用中新的開發工作,因爲單體應用中所做的所有更改都必須複製到微服務中。對於大多數公司來說,凍結應用程序開發工作可能存在風險,因爲他們必須根據業務環境的變化隨時調整軟件。
對於一個組織來說,從單體逐漸轉向微服務的路徑一定是更好的選項。下面列舉的一些可用設計模式可以幫助你逐步從單體架構轉向微服務架構。
扼殺者(Strangler Fig)
扼殺者是 Martin Fowler 設計的一種模式。它的靈感來自於自然界的無花果。無花果是從自己寄生的樹冠分枝開始生長的,其根向地面慢慢延伸。它的根會逐漸到達地面並繼續生長,甚至在這一過程中會殺死寄主樹。同樣,在軟件世界中,我們可以根據這種模式圍繞單個單體應用程序來構建微服務。我們會在系統中逐步添加更多微服務,最終有一天會替換掉整個單體系統。
在扼殺者模式中,我們圍繞現有單體架構的邊緣創建新服務。單體的邊緣是什麼意思?我們通過具體的例子來深入理解。
圖 1:單體應用
在圖 1 中,你可以看到我們有一個單體應用程序。在上圖中,產品庫存、訂單管理和計費管理模塊位於應用程序的邊緣。通知管理有多個來自應用程序內的入站調用。因此,我們無法將所有入站調用從外部應用程序重定向到通知管理。我們有另一種模式來將通知管理遷移到微服務,將在稍後討論。
假設我們想將訂單管理遷移到微服務。我們可以使用以下步驟。
-
插入代理:除非你已經有了一個代理,否則我們需要部署一個 HTTP 代理。在第 1 步中,我們部署一個 HTTP 代理,它將所有調用直接重定向到單體應用程序。引入 HTTP 代理後,你還可以瞭解網絡上是否存在其他內容會延遲 API 調用。如果延遲很大,那麼你必須先停止遷移並首先改進你的網絡,然後再繼續。
-
部署微服務:在第 2 步中,你將在生產環境中部署微服務。我們的微服務上不會有任何實時流量。在第 2 步中,我們將只測試微服務是否工作正常。
-
重定向流量:在第 3 步中,我們會將實際流量從 HTTP 代理重定向到我們新部署的微服務。如果出現問題,我們可以更改 HTTP 代理定向來輕鬆回滾。
所有步驟如圖 2 所示。
抽象分支
當你需要提取其他模塊所依賴的一個模塊時,抽象分支的模式可能會很有用。假設在前面的示例中,我們想將通知管理轉換爲微服務。在這種情況下,我們就會使用抽象分支。我們需要執行以下步驟來提取模塊。
-
創建抽象。你需要圍繞要替換的模塊創建抽象。
-
將現有功能的客戶端更改爲使用新抽象:你需要重構舊代碼,讓舊實現使用在步驟 1 中創建的抽象。
-
創建新的實現。你需要爲功能創建一個新的微服務實現,將其部署到生產環境中並運行一些測試。
-
切換實現。當你運行了一些測試,有了一些信心後,你就可以切換到新的代碼上。
-
清理。當你的微服務啓動並運行後,最好清理舊代碼庫並刪除舊模塊。如果需要,你也可以刪除抽象。在許多情況下,你之前創建的抽象只會改進你的代碼庫質量,這時完全可以保留它。
爲了更好地理解整個過程,請參考圖 3。
圖 3:抽象分支
抽象分支模式可以用在很多地方。我們建議儘可能使用 Strangler Fig 而不是抽象分支。如果你確定你不能用 Stranger Fig 來將單體應用的某些部分替換爲微服務,那麼你就應該考慮抽象分支。
並行運行
無論你做了多少測試,出現錯誤的可能性仍然會存在。當你遷移一個關鍵系統時,你一點都不能指望運氣。在這種情況下,並行運行模式可能會有所幫助。在這種模式中,我們會在生產環境部署我們新開發的微服務和舊的單體應用。我們會讓數據流經兩個系統。單體系統一開始會是唯一事實來源。我們將新開發的微服務的結果與單體結果做對比。如果我們發現存在任何不匹配情況,就要在微服務應用程序中修復它。一段時間後,當我們對新的微服務系統有足夠的信心時,就可以停用單體應用的對應功能,並讓微服務成爲唯一事實來源。
在前面的例子中,假設我們想將計費管理從單體遷移到微服務。在這個模式下,我們將開發一個微服務並將相同的流量發送到我們新的微服務。每天結束時,我們可以用一個批處理作業來對比舊系統和新系統生成的賬單是否相同。一旦我們有了足夠的信心,就可以從單體應用中停用計費管理功能。我們還有一些開源庫(比如 Github 的 scientist 庫),可以幫助你更好地實現這種模式。
圖 4:並行運行模式
當你的功能已經存在於單體應用中時,上面介紹的這種模式會很有用。假設你需要添加新的功能,比如你想在每次成功交易後通過電子郵件向用戶發送下一次交易的折扣券。很簡單,你只需在單體應用的訂單模塊中添加新代碼即可調用新創建的折扣微服務。但是如果你沒有代碼呢?假設你正在使用其他供應商的解決方案,或正在使用某些 SAAS,你也依舊可以實現它。接下來的兩種模式就是針對這種情況量身定製的。
裝飾協作者(Decorating Collaborator)
這種模式的靈感來自我們熟悉和喜愛的一種模式——裝飾者模式。在這種模式下,就像扼殺者模式一樣,我們必須引入一個代理。我們讓調用通過代理傳遞到單體應用,然後根據單體應用的響應,代理將調用我們新創建的微服務。
圖 5:裝飾協作者
圖 5 展示了裝飾協作者模式的機制。僅當微服務所需的所有數據都已存在於請求或響應中時,我們才應該使用這種模式。如果數據不存在,那麼我們新創建的微服務就必須連接到單體數據庫上。也就是說我們新的微服務需要與單體數據庫耦合,這絕不是一個好主意。
更改數據捕獲模式
在這種模式中,我們將對數據庫中發生的更改做出反應。比方說,我們想爲系統中創建的每個客戶創建一張會員卡。在這種模式下,我們可以監聽客戶表中的更改。一旦我們檢測到有新客戶創建了客戶表,我們就可以調用 Loyalty 微服務。然後這個微服務可以向客戶發放會員卡,並向他們發送包含詳細信息的電子郵件。你可以使用多種方法來監聽數據庫中的更改。你可以使用觸發器,也可以使用數據庫的事務日誌。還可以編寫一個每隔幾分鐘觸發並檢查數據庫中發生的更改的流程。
總 結
正確實現的微服務具有許多優勢。將你的單體應用程序轉換爲微服務並不是朝夕就能完成的工作。你還應該記住,轉換爲微服務不是一場競賽,而是一場漫長的馬拉松。它需要足夠的耐心,並且必須做出良好的架構決策纔行。
在本文中,我們討論了一些你可以使用的模式。大多數情況下,你需要應用多種模式才能將單體應用程序完全轉換爲微服務。最後,我只是建議,在遷移到微服務之前花點時間弄清楚你準備使用的策略,這種準備工作遲早會獲得回報的。
參考資料:
https://martinfowler.com/bliki/StranglerFigApplication.html
https://martinfowler.com/bliki/BranchByAbstraction.html
英文原文鏈接:
https://levelup.gitconnected.com/patterns-to-know-before-migrating-your-monolith-to-microservices-72fcbcc7846e
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/rTSjjiWMdKsSsOjdX2Rnyw