巨石(monolithic)架構雖有簡單、易上手的優點,但當應用與組織規模放大,團隊間的協調成本暴增、部署管線變成瓶頸,巨石就成了流暢交付的阻礙。在這類情境下,微服務架構(microservice architecture)往往是更好的選擇。

但什麼才算微服務架構?它真的是「一堆 100 行程式碼」或「兩週工作量」的小服務嗎?它如何促成流暢交付,又有什麼缺點?本節先給出明確定義,再探討其優勢與內建挑戰。

微服務架構的定義#

微服務架構是一種架構風格,將應用組織為「兩個或以上、設計期鬆耦合、可獨立部署的服務」的集合。

這個簡短定義其實包含四個關鍵概念。

概念一:微服務是一種架構風格#

如同維多利亞(Victorian)、裝飾藝術(Art Deco)等實體建築風格,定義建築物的整體外觀、佈局與材料;軟體架構風格(architectural style)定義應用該如何被組織。常見的軟體架構風格還包括:

  • 分層架構(layered architecture)
  • 六角架構(hexagonal architecture)
  • 巨石架構(monolithic architecture)

微服務,只是其中一種選擇。

概念二:應用由兩個以上的服務組成#

服務(service)是一個可部署單元(例如可執行 JAR 或 WAR 檔),實作一個或多個業務能力(business capability,亦稱子領域 subdomain)。

範例:一個簡單的微服務應用可能由三個服務組成:

  • Customer Service:處理消費者管理。
  • Order Service:處理訂單建立與生命週期。
  • Delivery Service:處理外送配送。
  • 並由一個 API Gateway 將外部流量路由到對應服務(第 12 章詳述)。

原本一個大型巨石應用,在微服務架構下被切成多個較小應用。它本質上是一個分散式架構——部分(但非全部)請求會跨越多個服務,例如:

  • POST /consumers(建立消費者):僅由 Customer Service 處理。
  • POST /orders(建立訂單):由 Order Service 起始,再與其他兩個服務協作。

Figure 1.8: A microservice architecture consists of two or more services that implement slices of business functionality

分散式特性是雙面刃:它帶來微服務的諸多優勢,也是諸多缺點的根源。

「微服務 ≠ 一堆小服務」

名稱中的「micro」與早期定義(例如 100 行程式碼或兩週工作量)讓很多人誤以為服務必須又多又小。也有些知名案例擁有數百甚至上千個服務,服務依賴圖長得像「死星(Death Star)」。

但這不該被盲目模仿。Chris Richardson 提出的判斷準則:服務數量超過團隊數量是危險訊號。一個服務存在的條件,是它解決了具體問題;通常較少、較大的服務反而是更好的選擇。

概念三:服務之間設計期鬆耦合#

設計期鬆耦合(loose design-time coupling)的意思是:修改一個服務,鮮少需要同步修改另一個服務。這帶來幾個好處:

  • 團隊之間主要透過 X-aaS 互動,僅在演化服務 API 時才需協作。
  • 雖然整個應用龐大複雜,服務開發者只需聚焦於相對小、相對單純的業務領域,日常體驗就像在開發一個小型應用。

Figure 1.9: The services in a microservice architecture are loosely design-time coupled

概念四:服務可獨立部署#

「可獨立部署」不只是「服務本身是可部署單元」這麼表層的意思,還包含:

  • 每個服務有自己的部署管線,獨立建置、測試、部署。
  • 不需要緩慢、易碎、難寫的端到端(end-to-end)測試牽涉到其他服務。
  • 因為服務由小型團隊開發,變更頻率相對低,部署管線少有延遲。
  • 結果:單一服務的變更可以快速、輕鬆地進入生產環境。

Figure 1.10: A service must be independently deployable: packaged, has its own pipeline, production-ready after isolated testing

為何微服務架構能促成流暢交付#

對於大型組織開發的大型複雜應用而言,微服務架構之所以能促成流暢交付,有兩個主要原因:高度團隊自主快速的部署管線

微服務促進團隊自主#

  • 服務之間鬆設計期耦合,單一團隊修改自己的服務時,不必與數十甚至上百個其他團隊協調。
  • 每個服務有獨立的技術棧(technology stack),團隊可自由變更——例如升級需要程式碼配合的函式庫。
  • 各團隊在不同的程式碼庫工作,意外干擾的機率降低:Orders team 弄壞自家 build,只是他們自己的問題,Delivery team 仍然可以正常部署變更。

微服務的部署管線又快又穩#

  • 每個服務有專屬的部署管線,變更頻率較低,程式碼庫小、測試快。
  • 服務隔離測試(test in isolation),沒有緩慢的端到端測試。
  • 處理時間(processing time)與等待時間(wait time)都顯著下降。
  • 服務通常小到可以在開發者筆電上完整測試,本地回饋快速。

Figure 1.11: A change to a service can be quickly deployed because every service has its own pipeline

微服務的其他好處#

除了促成流暢交付之外,微服務架構還有其他重要優勢。

支援多種技術棧#

  • 各服務可使用不同技術棧:例如多數服務用 Java,AI 相關服務用 Python。
  • 漸進式升級:一次升一個服務的技術棧,風險與時程都更可控。
  • 能以較低成本進行技術實驗。
  • 結果:即使應用要活十幾年甚至更久,技術棧仍能持續更新。

Figure 1.12: Different services can use different technology stacks

依特性將模組獨立部署#

具有不同特性(scalability、availability、security、開發便利性等)的模組,可被打包為獨立服務,以提升整體架構屬性。例如:

  • 將關鍵功能放在獨立服務,於高可用基礎設施上運行,與其他變更隔離,大幅提升可用性。

第 7 章將詳細說明這類「依特性切分模組」的策略。

沒有銀彈:微服務的缺點#

Fred Brooks 在 1986 年的《No Silver Bullet》中說過:「軟體工程沒有銀彈。」微服務架構同樣有它的代價。

主要的五項缺點:

  • 分散式運作(distributed operations)更難設計。
  • 容易誤打誤撞做出「分散式巨石」(distributed monolith)。
  • 設計一個好的微服務架構需要嚴謹與紀律,許多組織並不習慣。
  • 微服務需要與成功三角的另外兩項要素並行(DevOps + Team Topologies)。
  • 擁抱「獨立部署」是組織文化上的重大改變。

分散式運作更難設計#

部分操作會橫跨多個服務(例如 createOrder() 同時涉及 Order Service 與 Customer Service)。這帶來巨石架構不會遇到的問題:

  • 服務是行程邊界(process boundary),通訊必須透過網路;Order Service 必須處理 Customer Service 不可用的情境。
  • Fallacies of Distributed Computing」提醒我們網路並不免費。設計上要避免引入執行期耦合(runtime coupling,服務之間同步呼叫),否則會降低可用性、增加延遲,甚至演變成「聖誕燈架構」(Christmas tree light architecture)——一個服務故障就連鎖癱瘓整個系統。
  • 服務是交易邊界(transaction boundary),跨服務操作不能用單一資料庫交易實作,必須改用較陌生且較複雜的最終一致性(eventual consistency)模型,需要顯式撰寫補償邏輯與處理併發。

Figure 1.13: Some operations span multiple services, creating distributed-operation design challenges

風險:做出分散式巨石#

分散式巨石(distributed monolith)是一種反模式:服務之間設計期緊耦合,需要同步變更——同時擁有巨石與微服務的最壞特質。

在分散式巨石中,你不能對服務 API 做破壞性變更——那會直接導致生產事故。必須以漸進、向後相容的方式演化 API,工作量大、跨團隊協調成本高。

設計需要嚴謹與紀律#

避免分散式架構的諸多問題,關鍵在於慎重定義服務邊界:

  • 確保服務間鬆設計期耦合。
  • 把效能關鍵的操作收斂到單一服務內部。即使這意味著服務比直覺中的「微」要大,通常也是值得的取捨。

More the Merrier(越多越好)反模式:盲目把「micro」字面化、瘋狂切服務,會做出細粒度過頭、難以理解與維護的架構。網路呼叫的開銷拖垮效能,執行期耦合又讓系統脆弱不堪。

Chris Richardson 分享的真實故事:某個團隊有 5 個開發者、5 個服務,測試做得苦不堪言。他建議把 5 個服務合併為 1 個,測試問題迎刃而解。

設計微服務架構是一連串影響深遠的決策——哪個服務該實作哪個功能、是否該新增服務,都需要評估眾多權衡。第 20 章會介紹他的「Assemblage」設計流程,本書通篇也會介紹微服務模式語言(Microservice Pattern Language)。

必須與成功三角的另外兩個要素一起導入#

微服務架構若沒有 DevOps 與 Team Topologies 同步落地,很難兌現所宣傳的好處。但組織文化的改變極其困難——過去 20 多年,許多組織努力導入「Agile Manifesto」原則仍力有未逮。

沒有 DevOps 與 Team Topologies 的微服務,就像「買了法拉利,卻用馬拉著走」。

擁抱獨立部署是重大改變#

組織常難以放下「整體端到端測試後才能部署」的習慣:

  • 端到端測試牽涉太多元件,執行慢、隨機失敗多。
  • 仰賴人工審查決定變更能不能釋出,失去快速部署的承諾。
  • 解法:重新思考測試策略,把端到端測試移出部署管線