微服務、單體、微單體

同時克服單體和微服務架構限制的提議

Du Spirit
Java Magazine 翻譯系列
12 min readMar 8, 2024

--

Translated from “Microservice, monolith, microlith” by Vasily Strelnikov, Java Magazine, August 6, 2021. Copyright Oracle Corporation.

作為培訓顧問,我時常回答一些關於微服務相當務實的問題:它們是什麼?微服務有何特別之處?微服務最合理和有效益的使用案例是什麼?

通常,問題的答案相當片面,取決於個人過往的經驗和偏好。答案從「一切都應該是微服務」到「應該像瘟疫一樣避免微服務」都有,其中有不同程度的警告性觀點。

儘管有多種答案,但我發現它們通常缺乏科學精確性,僅代表觀點而非真的事實。實際上,許多建議是基於特定微服務實作案例的個人經驗證詞,描述其成功或失敗的情況。

本文試著呈現與這類軼事證據完全不同的東西。我將從眾多觀點以及關於微服務的本質和適用性的觀點中,探索一些硬性事實。

定義微服務架構

我們先從微服務實際上是什麼的定義開始。哦,等等,沒有明確 —— 至少沒有被普遍認可的定義。相反,有許多互相競爭的定義,它們似乎有一些共同的特徵。以下是一些微服務普遍認可的特徵:

  • 微服務的特徵是尺寸微小。這意味著部署佔用空間小,更容易測試、部署、維護和擴展微服務應用程式。較小的應用程式實現更短、更便宜的產品更新周期和更靈活的可擴展性。
  • 微服務被描述為鬆散耦合。每個微服務應用程式應該是獨自運作的商業邏輯單元,不依賴於其他應用程式。鬆散耦合也意味微服務應用程式應該能夠獨立進行版本控制和部署。
  • 每個微服務應該由一個小型團隊能選擇使用的技術來開發和維護。這種方法促進了緊密開發,專注在相對較小的商業功能子集上,進而實現更精確和可靠的商業邏輯實作。(見圖 1)
圖 1 — 微服務架構

定義單體架構

一個微服務並不會單獨存在以滿足自身的需求,而是成為一個廣泛服務集合的一部分,這些服務共同滿足組織的商業需求。

考慮到大型企業級應用程式,常被描述為單體架構。不幸地,如同微服務,「單體架構」這個詞並沒有嚴格的定義,所以不得不再次描述其特徵。

  • 單體架構被描述成一個實作企業內許多不同商業功能的大型應用程式。許多微服務提供的商業邏輯,由一個單體應用程式實作,但開發一個更大的應用程式將需要更長的時間。相較一組微服務,單體可能更難維護,且在可擴展性的選項較不彈性。
  • 單體應用程式內的元件可能緊密耦合,這意味著單體應用程式內部存在許多依賴關係。當然,這不是必然的,因為依賴關係的數量很大程度上取決於具體的設計和架構選擇。然而,內部依賴關係還是可能存在,因為當所有程式碼都屬於同一應用程式時,建立依賴關係對於開發人員來說不是一件難事。
  • 單體架構的開發是許多工程師和設計師的集體努力,使其開發周期更長,但在許多不同的商業功能中,可能促進一致的設計。在整個企業中使用共同的技術可以簡化維護和開發。與微服務倡導者提倡的多語言方法不同,單體開發不允許針對個別商業功能選擇最佳的設計和架構的靈活性。(見 Figure 2.)
圖 2 — 單體架構

常見誤解

以下是一些關於微服務和單體架構的常見誤解。

服務粒度。服務粒度描述商業的功能在多個服務中的分佈情況。例如,假設要建立一個描述科學實驗並記錄其測量結果的商業功能。這商業功能可以用一個單獨的服務實現,以一個結合所有屬性和所有相關測量的大型物件來處理。這種方法通常被稱為粗粒度的服務設計。

另一種方法是細粒度的服務設計。完全相同的商業功能以多個不同的服務實現,分別處理實驗或測量等較小的數據。注意,這兩種方法在實現相同的商業功能時,服務的數量有所不同。換句話說,兩種方法都實現相同的邏輯,但是公開的服務數量不同。

問題在於,服務粒度常常與微服務的概念混淆:基本上,細粒度的服務設計不一定以微服務實作,而粗粒度的服務設計也不一定是單體架構。

理解這些概念不是同義的關鍵在於服務最基本的一個特性有關,即服務調用者不應該知道服務的實作細節。因此,對於服務消費者來說,服務的實作方式並不重要。無論是單體或是其他形式,服務消費者都無法分辨出差異。

為了解決混淆,我建議使用「實作粒度」代替「服務粒度」,其中實作粒度可以是細粒度或粗粒度。專注於界定服務介面背後應用程式實際的複雜性和大小。實作粒度的概念應有助於澄清混淆。您可以將微服務描述成基於細粒度的實作設計,並允許開發人員根據需要提供任何粒度的服務。

問題在於微服務暗示必須以小型應用程式實作,這可以描述為細粒度的實作設計。

記住,小尺寸和鬆散耦合是微服務的重要特徵,帶來更短的產品更新周期、更彈性的可擴展性、獨立的版本控管和部署。然而,這些好處不該視為是自動獲得的。

資料碎片化。一個細粒度應用程式實作的意外結果是資料碎片化。鬆散耦合的設計意味著每個微服務都有自己的資料存儲,包含該特服務專屬的資訊。

鬆散耦合的設計,隱含不同的服務之間不應該使用分佈式交易 (distributed transactions) 或兩段式提交 (two-phase commit) 來同步資料,以保持微服務應用程式間的高度分離。這方法引入資料碎片化問題和潛在的一致性缺失。

譯註:這裡把原文中的 (micro)-service 與 application 都翻成 (微)服務,讀起來比較通順一點。

假定一個微服務需要另一個微服務擁有的信息。若解決方案僅是允許一個服務呼叫另一個服務以獲取或同步所需的資訊呢?這可能有效,但如果有個服務遇到性能問題或故障怎麼辦?不可避免地,這對任何其他依賴的服務產生連鎖效應,導致更大規模的故障和整體性能下降。這種方法可能適用於少量的服務,但服務的集合越大,對性能和可用性的風險就越大。

因此,考慮另一種解決資料一致性和碎片化影響的方案。例如,每個微服務都快取需要從其他服務取得的資訊呢?

譯註:這裡的快取不限於使用 (分散式) 記憶體技術的快取,建立本地資料表的副本也算是一種不跨服務的快取。

快取確實為每個服務提供一定程度的自主性,有助於提升隔離和自主運行的能力。然而,快取亦意味著服務在設計時,須考慮到資料的狀態並不總是最新的。利用不同的分佈式快取和資料串流解決方案,自動建立資料副本。最後,每個服務都必須提供資料狀態追蹤和撤銷 (undo) 的能力,而不是分散式交易同步。

不可避免地,這些問題導致設計更加複雜,讓每個微服務看起來不像一開始那樣簡單。

此外,每個在特定微服務上工作的開發團隊,必須維護與其他服務的資料依賴性,無法真的與其他團隊完全隔離。

換句話說,微服務架構似乎並未實際解決單體架構所面臨的相依性問題。相反,資料一致性和完整性管理本來是單個單體應用的內部問題,轉變成多個微服務開發團隊的共同責任。

版本控管。另一個問題源於每個微服務具獨立版本控管能力的承諾。在複雜的服務交互場景中,功能上依賴關係必須考慮資料的依賴關係。

想像有個服務,其中內部的實作已經修改。這修改不一定會導致服務介面或資料格式發生任何變化。許多開發人員可能不認為這樣實作的修改會需要升版,因此也不會通知依賴的開發人員有這些變化。

然而,這樣的修改可能會影響服務解釋其資料的語義,導致微服務之間存在差異。

例如,來看特定值的解釋發生變化的影響。假設一個記錄測量值的應用程式將英寸視為預設的度量單位,其他應用程式可能依賴於這個預設值。內部的更動可能將預設值解釋為公分而不是英寸。這可能對任何其他微服務產生連鎖影響,總的來說,甚至可能是危險的。然而,其他系統的開發人員對於這種變動可能一無所知。

譯註:這其實跟微服務無關,即便是單體架構,某個元件介面不變,實作改變語意,在沒有好的溝通情況下,問題也是會發生。

這意味著,在任何非顯而易見的應用程式互動場景中,微服務的特徵不該被自動認為是有益無害的。

單體架構與微服務架構面對相似的問題

不管選擇哪一種架構,企業都面對完全相同的功能和資料整合問題。無論如何,單體架構和微服務架構都得解決這些問題,所以問題其實是要理解每種方法的利弊。

  • 單體架構其集中式設計和統一開發的方法,提供了資料和功能的一致性,但代價是可擴展性和彈性。
  • 微服務提供相當程度的開發自主性,但將解決資料和功能一致性問題的責任轉移給不同的獨立開發團隊,這可能是一個非常棘手的協調任務。

也許有一種平衡的方法,可能是未來的方向,旨在兼顧微服務和單體架構的優點並減輕其缺點。在我看來,最關鍵的因素是實作粒度的概念,正如之前討論的那樣。

REST 服務是呈現微服務最常見的形式,大多數 REST 服務的使用案例都聚焦於一個服務代表一個單一的商業實體。這方式導致極其細粒度的服務實作。

考慮到需要在多個獨立管理的商業實體間維護資料一致性,這些應用程式間的依賴關係增加了。

然而,嚴格來說,微服務架構並沒有要求如此細微的實作粒度。事實上,微服務通常是指專注於單一商業能力,這與單一商業實體不見得相同,因為單一商業能力可能需要多個商業實體。

譯註:這也是最近 Microservice 常常與 DDD 放在一起討論的原因,DDD 裡提到的 boundary context,是蠻適合做為一個 microservice 的大小。

通常,這些實體形成資料組,彼此之間存在非常緊密的聯繫和大量的依賴關係。以資料組作為指導原則來決定應用程式的實作粒度,應該會產生數量較少的微服務應用程式,能更好地隔離。每個這樣的應用程式,相較於一應用程式一實體的結構不算是真正的微小,但也不是一個將整個企業功能納入其中的單一單體。

這種方法應能減少跨應用程式資訊同步的需求,實際上,這可能對整個系統的性能和可靠性產生正面的影響。

商業能力。什麼構成單一商業能力?很明顯,這聽起來像是一組常用的商業功能,但這仍是相當模糊的定義。值得考慮的是商業功能如何使用資料作為分組原則。例如,資料可以以在特定商業流程的情境中產生的一組資料物件分組,並具有共同的所有權。

共同擁有權意味著有一個特定的業務單位負責數個商業實體。共同擁有權還意味著該單位的商業決策會定義實體的語義情境,可能影響其資料結構,最重要的是,定義一組負責建立、修改和刪除這些資料的業務功能。

其他業務單位可能希望讀取相同資訊,但它們大多作為這些資料的消費者而不是生產者。因此,每個應用程式將負責其自己的一部分商業實體,並能夠在本地執行所有必需的交易,無需進行分散式交易協調或兩段式提交操作。

當然,資料副本在應用程式之間的仍是需要,作為快取,以提高個別應用程式的自主性。然而,維護一組唯讀的資料副本的開銷要遠小於維護多方向資料同步的開銷。此外,由於應用程式整體數量減少,因此需要進行的資料複製也會減少。

資料擁有權。在大型企業中,資料擁有權的問題可能很難解決,需要對資料和商業流程的分析進行一些投資。對更廣泛資料背景的了解有助於確定資料的來源以及可能的資料使用者。分析圖必須呈現比個別微服務更廣的視野。

實際上,除了一些專注特定應用程式的開發團隊外,還需要建立一個額外的設計師和分析師小組來建立及維護一個整合的企業資料模型,協助界定個別應用程式的範圍,並協調所有開發團隊之間的任何差異。

正如你所見,這種方法建議借用一些單體應用程式的設計特性,但以不同的方式使用,目標不是建立一個企業級的單一應用程式,而是支持許多應用程式的整合,每個應用程式都專注於實作其特定的商業能力。

此外,這種方法旨在確保服務的邊界被清晰定義和維護,並建議根據資料擁有權原則作為標準建立邊界。甚至還有一些有趣的 API,例如 GraphQL,可以支持這些整合工作。

名稱。還有一個問題要解決:我們該如何稱呼這架構?即使一些商業能力可能擁有相對較多的實體,因此,一些應用程式實際上可能並不那麼小,它仍應該被稱為微服務嗎?

我想建議將這方法稱為微單體架構,呈現這種策略的混合性質,試圖結合微服務和單體架構的優點。實際上,microlith 一詞意味著小型石頭工具,例如史前的箭頭或用石頭製成的針。我喜歡這個詞所傳達的實用概念。(見圖 3)

圖 3 — 微單體架構

我相信許多人會同意,最佳的設計和架構決策是基於實際的成本效益分析,而不是盲目遵循抽象原則。

深入研究

譯者的告白

最近比較少翻譯 Java Magazine 的文章了,最主要是 ChatGPT 在這方面已經非常成功了,這篇也是先用 ChatGPT 翻譯,然後以個人的風格修飾。會選擇這一篇,主要是前陣子回母校,學校的老師有提到系上有開微服務相關的課程,但說真的,我自己的觀察是,很多人在嘗試微服務開發,一開始有享受到好處,但如果顆粒度沒拿捏好,後面光是管理眾多的微服務就頭痛了,於是又回到單體架構。營運系統不光是開發而已,還有很多管理上的事... 趨勢就跟流行一樣,一下子 A 一下子 B,哪天又回到 A 了。

--

--