天天看點

微服務需要拆分到什麼程度?

微服務需要拆分到什麼程度?

作者:克裡斯·理查森

譯者:喻勇

來源:《微服務架構設計模式》經出版社授權釋出

導讀:微服務在最近幾年大行其道,很多公司的研發人員都在考慮微服務架構,或者在做微服務的路上,拆分服務是個很熱的話題。那麼我們應該按照什麼原則将現有的業務進行拆分?是否拆分得越細就越好?本文将研究把應用程式分解為服務的政策和指南、分解的障礙以及如何解決它們。

01 服務拆分政策

1. 根據業務能力進行服務拆分和定義

建立微服務架構的政策之一就是采用業務能力進行服務拆分。業務能力是一個來自于業 務架構模組化的術語。業務能力是指一些能夠為公司(或組織)産生價值的商業活動。特定業務的業務能力取決于這個業務的類型。例如,保險公司業務能力通常包括承保、理賠管理、 賬務和合規等。線上商店的業務能力包括:訂單管理、庫存管理和發貨,等等。

識别業務能力

組織的業務能力通常是指這個組織的業務是做什麼,它們通常都是穩定的。與之相反,組織采用何種方式來實作它的業務能力,是随着時間不斷變化的。這一個組織有哪些業務能力,是通過對組織的目标、結構和商業流程的分析得來的。每一 個業務能力都可以被認為是一個服務,除非它是面向業務的而非面向技術的。業務能力規範 包含多項元素,比如輸入和輸出、服務等級協定(SLA)。例如,保險承保能力的輸入來自客 戶的應用程式,這個業務能力的輸出是完成核保并報價。

業務能力通常集中在特定的業務對象上。例如,理賠業務對象是理賠管理功能的重點。 能力通常可以分解為子能力。例如,理賠管理能力具有多個子能力,包括理賠資訊管理、理賠稽核和理賠付款管理。

把 FTGO 的業務能力逐一列出來似乎也并不太困難,如下所示。

  • 供應商管理。
  • Courier management:送餐員相關資訊管理;
  • Restaurant information management:餐館菜單和其他資訊管理,例如營業位址和時間。
  • 使用者管理:使用者有關資訊的管理。
  • 訂單擷取和履行。
  • Order management:讓使用者可以建立和管理訂單。
  • Restaurant order management:讓餐館可以管理訂單的生産過程。

物流。

  • Courier availability management:管理送餐員的實時狀态。
  • Delivery management:把訂單送到使用者手中。
  • 會計記賬。
  • Consumer accounting:管理跟使用者相關的會計記賬。
  • Restaurant accounting:管理跟餐館相關的會計記賬。
  • Courier accounting:管理跟送餐員相關的會計記賬。
  • 其他

頂級能力包括供應商管理、使用者(消費者)管理、訂單擷取和履行以及會計記賬。可能 還有許多其他頂級能力,包括與營銷相關的能力。大多數頂級能力都會分解為子能力。例 如,訂單擷取和履行被分解為五個子能力。 這個能力層次的有趣方面是有三個餐館相關的功力:餐館資訊管理、餐館訂單管理和餐 館會計記賬。那是因為它們代表了餐館營運的三個截然不同的方面。 接下來,我們将了解如何使用業務能力來定義服務。

從業務能力到服務

一旦确定了業務能力,就可以為每個能力或相關能力組定義服務。圖1顯示了 FTGO 應用程式從能力到服務的映射。決定将哪個級别的能力層次結構映射到服務是一個非常主觀的判斷。我對這種特定映射的理由如下:

  • 我将供應商管理的子能力映射到兩種服務,因為餐館和送餐員是非常不同類型的供應商。
  • 我将訂單擷取和履行能力映射到三個服務,每個服務負責流程的不同階段。我将送餐員可用性管理(Courier availability management)和傳遞管理(Delivery management)能力結合起來,并将它們映射到單個服務,因為它們交織在一起。
  • 我将會計記賬能力映射到自己的獨立服務,因為不同類型的會計記賬看起來很相似。
微服務需要拆分到什麼程度?

圖1 将 FTGO 業務能力映射到服務。能力層次結構各個級别的能力都映射到服務

圍繞能力組織服務的一個關鍵好處是,因為它們是穩定的,是以最終的架構也将相對穩定。架構的各個元件可能會随着業務的具體實作方式的變化而發展,但架構仍保持不變。

話雖如此,重要的是要記住圖1 中顯示的服務僅僅是定義架構的第一次嘗試。随着我 們對應用程式領域的了解越來越多,它們可能會随着時間的推移而變化,特别是架構定義流 程中的一個重要步驟是調查服務如何在每個關鍵架構服務中協作。例如,你可能會發現由于過多的程序間通信而導緻特定的分解效率低下,導緻你必須把一些服務組合在一起。相反, 服務可能會在複雜性方面增長到值得将其拆分為多個服務的程度。圖2 展示了子域和服務之間的映射,每一個子域都有屬于它們自己的領域模型。

微服務需要拆分到什麼程度?

圖2從子域到服務,FTGO 應用程式域的每個子域都映射為一個服務,該服務有自己的領域模型

DDD 和微服務架構簡直就是天生一對。DDD 的子域和限界上下文的概念,可以很好地跟微服務架構中的服務進行比對。而且,微服務架構中的自治化團隊負責服務開發的概念,也跟 DDD 中每個領域模型都由一個獨立團隊負責開發的概念吻合。更有趣的是,子域用于它自己的領域模型這個概念,為消除上帝類和優化服務拆分提供了好辦法。

2. 根據子域進行拆分

領域驅動為每一個子域定義單獨的領域模型。子域是領域的一部分,領域是 DDD 中用來描述應用程式問題域的一個術語。識别子域的方式跟識别業務能力一樣:分析業務并識别業務的不同專業領域,分析産出的子域定義結果也會跟業務能力非常接近。FTGO 的子域包括:訂單擷取、訂單管理、餐館管理、送餐和會計。正如你所見:這些子域跟我們之前定義的業務能力非常接近。

DDD 把領域模型的邊界稱為限界上下文(bounded contest)。限界上下文包括實作這個模型的代碼集合。當使用微服務架構時,每一個限界上下文對應一個或者一組服務。換一種說法,我們可以通過 DDD 的方式定義子域,并把子域對應為每一個服務,這樣就完成了微服務架構的設計工作。

按子域分解和按業務能力分解是定義應用程式的微服務架構的兩種主要模式。但是,也有一些有用的拆分指導原則源于面向對象的設計。我們來詳細讨論這些原則。

02 拆分的指導原則

單一職責原則

軟體架構和設計的主要目标之一是确定每個軟體元素的職責。單一職責原則如下:

改變一個類應該隻有一個理由。

—Robert C. Martin

類所承載的每一個職責都是對它進行修改的潛在原因。如果一個類承載了多個職責,并且互相之間的修改是獨立的,那麼這個類就會變得非常不穩定。遵照 SRP 原則,你所定義的每一個類都應該隻有一個職責,是以也就隻有一個理由對它進行修改。

我們在設計微服務架構時應該遵循 SRP 原則,設計小的、内聚的、僅僅含有單一職責的服務。這會縮小服務的大小并提升它的穩定性。新的FTGO架構是應用SRP的一個例子。

為客戶擷取餐食的每一個方面(訂單擷取、訂單準備、送餐等)都由一個單一的服務承載。

閉包原則

另外一個有用的原則是閉包原則(CCP):

在包中包含的所有類應該是對同類的變化的一個集合,也就是說,如果對包做出修改,需要調整的類應該都在這個包之内。

—— Robert C. Martin

這就意味着,如果由于某些原因,兩個類的修改必須耦合先後發生,那麼就應該把它們 放在同一個包内。也許,這些類實作了一些特定的業務規則的不同方面。這樣做的目标是當 業務規則發生變化時,開發者隻需要對一個傳遞包做出修改,而不是大規模地修改(和重新 編譯)整個應用采用閉包原則,極大地改善了應用程式的可維護性。

在微服務架構下采用 CCP原則,這樣我們就能把根據同樣原因進行變化的服務放在一 個元件内。這樣做可以控制服務的數量,當需求發生變化時,變更和部署也更加容易。理想 情況下,一個變更隻會影響一個團隊和一個服務。CCP 是解決分布式單體這種可怕的反模式的法寶。

單一職責原則和閉包原則是 Bob Martin 制定的十一項原則中的兩項。它們在開發微服務架構時特别有用。在設計類和包時可以使用其餘的九個原則。有關這些原則的更多資訊,請參閱 Bob Martin 網站上的文章《面向對象設計的原 則》

http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

03 拆分單體應用為服務的難點及解決方案

從表面上看,通過定義與業務能力或子域相對應的服務來建立微服務架構的政策看起來很簡單。但是,你可能會遇到幾個障礙:

  • 網絡延遲。
  • 同步程序間通信導緻可用性降低。
  • 在服務之間維持資料一緻性。
  • 擷取一緻的資料視圖。
  • 上帝類阻礙了拆分。

網絡延遲

網絡延遲是分布式系統中一直存在的問題。你可能會發現,對服務的特定分解會導緻兩個服務之間的大量往返調用。有時,你可以通過實施批處理API在一次往返中擷取多個對象,進而将延遲減少到可接受的數量。但在其他情況下,解決方案是把多個相關的服務組合在一起,用程式設計語言的函數調用替換昂貴的程序間通信。

同步的程序間通信導緻可用性降低

另一個需要考慮的問題是如何處理程序間通信而不降低系統的可用性。例如,實作createOrder() 操作最常見的方式是讓Order Service使用REST同步調用其他服務。這樣做的弊端是REST這樣的協定會降低Order Service的可用性。如果任何一個被調用的服務處在不可用的狀态,那麼訂單就無法建立了。有時候這可能是一個不得已的折中,但是在學習異步消息之後,你就會發現其實有更好的辦法來消除這類同步調用産生的緊耦合并提升可用性。

在服務之間維持資料一緻性

另一個挑戰是如何在某些系統操作需要更新多個服務中的資料時,仍舊維護服務之間的資料一緻性。傳統的解決方案是使用基于兩階段送出(two phase commit)的分布式事務管理機制。但對于現今的應用程式而言,這不是一個好的選擇,你必須使用一種非常不同的方法來處理事務管理,這就是Saga。Saga是一系列使用消息協作的本地事務。Saga 比傳統的ACID事務更複雜,但它們在許多情況下都能工作得很好。Saga 的一個限制是它們最終是一緻的。如果你需要以原子方式更新某些資料,那麼它必須位于單個服務中,這可能是分解的障礙。

擷取一緻的資料視圖

分解的另一個障礙是無法跨多個資料庫獲得真正一緻的資料視圖。在單體應用程式中,ACID 事務的屬性保證查詢将傳回資料庫的一緻視圖。相反,在微服務架構中,即使每個服務的資料庫是一緻的,你也無法獲得全局一緻的資料視圖。如果你需要一些資料的一緻視圖,那麼它必須駐留在單個服務中,這也是服務分解所面臨的問題。幸運的是,在實踐中這很少帶來真正的問題。

上帝類阻礙了拆分

分解的另一個障礙是存在所謂的上帝類。上帝類是在整個應用程式中使用的全局類上帝類通常為應用程式的許多不同方面實作業務邏輯。它有大量字段映射到具有許多列的資料庫表。大多數應用程式至少有一個這樣的上帝類,每個類代表一個對領域至關重要的概念:銀行賬戶、電子商務訂單、保險政策,等等。因為上帝類将應用程式的許多不同方面的狀态和行為捆綁在一起,是以将使用它的任何業務邏輯拆分為服務往往都是一個不可逾越的障礙。

Order類是FTGO應用程式中上帝類的一個很好的例子。Order 類具有與訂單處理、餐館訂單管理、送餐和付款相對應的字段及方法。由于一個模型必須描述來自應用程式的不同部分的狀态轉換,是以該類還具有複雜的狀态模型。在目前情況下,這個類的存在使得将代碼分割成服務變得極其困難。

一種解決方案是将 Order 類打包到庫中并建立一個中央Order資料庫。處理訂單的所有服務都使用此庫并通路通路資料庫。這種方法的問題在于它違反了微服務架構的一個關鍵原則,并導緻我們特别不願意看到的緊耦合。例如,對Order模式的任何更改都要求其他開發團隊同步更新和重新編譯他們的代碼。

另一種解決方案是将Order資料庫封裝在Order Service中,該服務由其他服務調用以檢索和更新訂單。該設計的問題在于這樣的一個Order Service将成為一個純資料服務,成為包含很少或沒有業務邏輯的貧血領域模型(anaemic domain model)。這兩種解決方案都沒有吸引力,但幸運的是,DDD 提供了一個好的解決方案。

更好的方法是應用DDD并将每個服務視為具有自己的領域模型的單獨子域。這意味着FTGO應用程式中與訂單有關的每個服務都有自己的領域模型及其對應的 Order 類的版本。Delivery Service是多領域模型的一個很好的例子。如圖 3所示為Order,它非常簡單:取餐位址、取餐時間、送餐位址和送餐時間。此外,Delivery Service 使用更合适的Delivery名稱,而不是稱之為Order。Delivery Service 對訂單的任何其他屬性不感興趣。

微服務需要拆分到什麼程度?

圖3 Delivery Service 的領域模型

除了造成一些技術挑戰以外,擁有多個領域模型還會影響使用者體驗。在定義微服務架構時必須識别并消除上帝類。

本文摘自《微服務架構設計模式》,經出版方授權釋出。

文章來源:微信公衆号 華章計算機