架構師必須掌握的重要微服務架構設計模式!

2021-03-07 12:00:46

作為一名合格的架構師,要掌握很多方面的技能和能力,其中架構的設計能力是必須具備的能力之一。在架構設計的過程中,架構師會使用一些重要的架構設計模式來進行架構設計。

近年來,微服務架構由於其「微」的特性,給架構帶來了更大的靈活性和便利性,也因此受到了很多架構師的青睞和使用。

本文重點介紹一下微服務架構的定義及其重要的設計模式,並介紹每種模式的優點,缺點,適用場景和不適用的場景,及其可用實現的技術有哪些,還有相應的擴充套件文章,希望對已經是架構師或者打算成為架構師的小夥伴有指導性幫助!

 

微服務架構定義

Martin Fowler的這篇文章《Microservices》通俗易懂的講解了什麼是微服務架構.

微服務架構是一種架構模式,它提倡將單一應用程式劃分成一組小的服務,服務之間互相協調、互相配合,為使用者提供最終價值。每個服務執行在其獨立的程序中,服務與服務間採用輕量級的通訊機制互相共同作業(通常是基於HTTP協定的RESTful API)。每個服務都圍繞著具體業務進行構建,並且能夠被獨立的部署到生產環境、類生產環境等。

 

微服務架構的重要特徵:

  • 整個應用程式被拆分成相互獨立但包含多個內部模組的子程序。

  • 與模組化的單體應用(Modular Monoliths)或 SOA 相反,微服務應用程式根據業務範圍或領域垂直拆分。

  • 微服務邊界是外部的,微服務之間通過網路呼叫(RPC 或訊息)相互通訊。

  • 微服務是獨立的程序,它們可以獨立部署。

  • 它們以輕量級的方式進行通訊,不需要任何智慧通訊通道。

 

微服務架構的優點:

  • 更好的開發規模。

  • 更快的開發速度。

  • 支援迭代開發或現代化增量開發。

  • 充分利用現代軟體開發生態系統的優勢(雲、容器、 DevOps、Serverless)。

  • 支援水平縮放和細粒度縮放。

  • 小體量,較低了開發人員的認知複雜性。

 

微服務架構的缺點:

  • 更高數量級的活動元件(服務、資料庫、程序、容器、框架)。

  • 複雜性從程式碼轉移到基礎設施。

  • RPC 呼叫和網路通訊的大量增加。

  • 整個系統的安全性管理更具有挑戰性。

  • 整個系統的設計變得更加困難。

  • 引入了分散式系統的複雜性。

 

何時使用微服務架構:

  • 大規模 Web 應用開發。

  • 跨團隊企業級應用共同作業開發。

  • 長期收益優先於短期收益。

  • 團隊擁有能夠設計微服務架構的軟體架構師或高階工程師。

 

以下是架構師必須掌握的重要微服務架構設計模式:

  • 獨享資料庫(Database per Microservice)
  • 事件源(Event Sourcing)
  • 命令和查詢職責分離(CQRS)
  • Saga
  • 面向前端的後端 (BFF)
  • API 閘道器
  • Strangler
  • 斷路器
  • 外部化設定
  • 消費端驅動的契約測試

 

獨享資料庫(Database per Microservice)

當一家公司將大型單體系統替換成一組微服務,首先要面臨的最重要決策是關於資料庫。單體架構會使用大型中央資料庫。即使轉移到微服務架構許多架構師仍傾向於保持資料庫不變。雖然有一些短期收益,但它卻是反模式的,特別是在大規模系統中,微服務將在資料庫層嚴重耦合,整個遷移到微服務的目標都將面臨失敗(例如,團隊授權、獨立開發等問題)。

更好的方法是為每個微服務提供自己的資料儲存,這樣服務之間在資料庫層就不存在強耦合。這裡我使用資料庫這一術語來表示邏輯上的資料隔離,也就是說微服務可以共用物理資料庫,但應該使用分開的資料結構、集合或者表,這還將有助於確保微服務是按照領域驅動設計的方法正確拆分的。

 

 

Md Kamaruzzaman 的微服務獨享資料庫

優點

  • 資料由服務完全所有。

  • 服務的開發團隊之間耦合度降低。

缺點

  • 服務間的資料共用變得更有挑戰性。

  • 在應用範圍的保證 ACID 事務變得困難許多。

  • 細心設計如何拆分單體資料庫是一項極具挑戰的任務。

何時使用獨享資料庫

  • 在大型企業應用程式中。

  • 當團隊需要完全把控微服務以實現開發規模擴充套件和速度提升。

何時不宜使用獨享資料庫

  • 在小規模應用中。

  • 如果是單個團隊開發所有微服務。

可用技術範例

所有 SQL、 NoSQL 資料庫都提供資料的邏輯分離(例如,單獨的表、集合、結構、資料庫)。

 

事件源(Event Sourcing)

在微服務架構中,特別使用獨享資料庫時,微服務之間需要進行資料交換。對於彈性高可伸縮的和可容錯的系統,它們應該通過交換事件進行非同步通訊。在這種情況,您可能希望進行類似更新資料庫並行送訊息這樣的原子操作,如果在巨量資料量的分散式場景使用關聯式資料庫,您將無法使用兩階段鎖協定(2PL),因為它無法伸縮。而 NoSQL 資料庫因為大多不支援兩階段鎖協定甚至無法實現分散式事務。

在這些場景,可以基於事件的架構使用事件源模式。在傳統資料庫中,直接儲存的是業務實體的當前「狀態」,而在事件源中任何「狀態」更新事件或其他重要事件都會被儲存起來,而不是直接儲存實體本身。這意味著業務實體的所有更改將被儲存為一系列不可變的事件。因為資料是作為一系列事件儲存的,而非直接更新儲存,所以各項服務可以通過重放事件儲存中的事件來計算出所需的資料狀態。

Md Kamaruzzaman 的事件源

優點

  • 為高可伸縮系統提供原子性操作。

  • 自動記錄實體變更歷史,包括時序回溯功能。

  • 鬆耦合和事件驅動的微服務。

缺點

  • 從事件儲存中讀取實體成為新的挑戰,通常需要額外的資料儲存(CQRS 模式)。

  • 系統整體複雜性增加了,通常需要領域驅動設計

  • 系統需要處理事件重複(冪等)或丟失。

  • 變更事件結構成為新的挑戰。

何時使用事件源

  • 使用關聯式資料庫的、高可伸縮的事務型系統。

  • 使用 NoSQL 資料庫的事務型系統。

  • 彈性高可伸縮微服務架構。

  • 典型的訊息驅動或事件驅動系統(電子商務、預訂和預約系統)。

何時不宜使用事件源

  • 使用 SQL 資料庫的低可伸縮性事務型系統

  • 在服務可以同步交換資料(例如,通過 API)的簡單微服務架構中。

可用技術範例

事件儲存: EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, Azure Cosmos DB, MongoDB, CassandraAmazon DynamoDB

框架: Lagom, Akka, Spring, akkatecture, AxonEventuate

延伸閱讀

事件驅動

事件驅動模式-雲設計模式

微服務模式:事件驅動

 

命令和查詢職責分離(CQRS)

如果我們使用事件源,那麼從事件儲存中讀取資料就變得困難了。要從資料儲存中獲取實體,我們需要處理所有的實體事件。有時我們對讀寫操作還會有不同的一致性和吞吐量要求。

這種情況,我們可以使用 CQRS 模式。在該模式中,系統的資料修改部分(命令)與資料讀取部分(查詢)是分離的。而 CQRS 模式有兩種容易令人混淆的模式,分別是簡單的和高階的。

在其簡單形式中,不同實體或 ORM 模型被用於讀寫操作,如下所示:

 

Md Kamaruzzaman 的 CQRS (簡單)

它有助於強化單一職責原則和分離關注點,從而實現更簡潔的設計。

在其高階形式中,會有不同的資料儲存用於讀寫操作。高階的 CQRS 通常結合事件源模式。根據不同情況,會使用不同型別的寫資料儲存和讀資料儲存。寫資料儲存是「記錄的系統」,也就是整個系統的核心源頭。

Md Kamaruzzaman 的 CQRS(高階)

對於讀頻繁的應用程式或微服務架構,OLTP 資料庫(任何提供 ACID 事務保證的關係或非關聯式資料庫)或分散式訊息系統都可以被用作寫儲存。對於寫頻繁的應用程式(寫操作高可伸縮性和大吞吐量),需要使用寫可水平伸縮的資料庫(如全球託管的公共雲資料庫)。標準化的資料則儲存在寫資料儲存中。

對搜尋(例如 Apache Solr、Elasticsearch)或讀操作(KV 資料庫、檔案資料庫)進行優化的非關聯式資料庫常被用作讀儲存。許多情況會在需要 SQL 查詢的地方使用讀可伸縮的關聯式資料庫。非標準化和特殊優化過的資料則儲存在讀儲存中。

資料是從寫儲存非同步複製到讀儲存中的,所以讀儲存和寫儲存之間會有延遲,但最終是一致的。

優點

  • 在事件驅動的微服務中資料讀取速度更快。

  • 資料的高可用性。

  • 讀寫系統可獨立擴充套件。

缺點

  • 讀資料儲存是弱一致性的(最終一致性)。

  • 整個系統的複雜性增加了,混亂的 CQRS 會顯著危害整個專案。

何時使用 CQRS

  • 在高可延伸的微服務架構中使用事件源。

  • 在複雜領域模型中,讀操作需要同時查詢多個資料儲存。

  • 在讀寫操作負載差異明顯的系統中。

何時不宜使用 CQRS

  • 在沒有必要儲存大量事件的微服務架構中,用事件儲存快照來計算實體狀態是一個更好的選擇。

  • 在讀寫操作負載相近的系統中。

可用技術範例

寫儲存: EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, Azure Cosmos DB, MongoDB, CassandraAmazon DynamoDB

讀儲存:Elastic Search, Solr, Cloud Spanner, Amazon Aurora, Azure Cosmos DB, Neo4j

框架:Lagom, Akka, Spring, akkatecture, Axon, Eventuate

延伸閱讀

bliki:CQRS

CQRS模式 - Azure 架構中心

微服務模式:命令和查詢職責分離(CQRS

 

Saga

如果微服務使用獨享資料庫,那麼通過分散式事務管理一致性是一個巨大的挑戰。你無法使用傳統的兩階段提交協定,因為它要麼不可伸縮(關聯式資料庫),要麼不被支援(多數非關聯式資料庫)。

但您還是可以在微服務架構中使用 Saga 模式實現分散式事務。Saga 是 1987 年開發的一種古老模式,是關聯式資料庫中關於大事務的一個替代概念。但這種模式的一種現代變種對分散式事務也非常有效。Saga 模式是一個本地事務序列,其每個事務在一個單獨的微服務內更新資料儲存並行佈一個事件或訊息。Saga 中的首個事務是由外部請求(事件或動作)初始化的,一旦本地事務完成(資料已儲存在資料儲存且訊息或事件已釋出),那麼釋出的訊息或事件則會觸發 Saga 中的下一個本地事務。

 

 

Md Kamaruzzaman 的 Saga

如果本地事務失敗,Saga 將執行一系列補償事務來回滾前面本地事務的更改。

Saga 事務協調管理主要有兩種形式:

  • 事件編排 Choreography:分散協調,每個微服務生產並監聽其他微服務的事件或訊息然後決定是否執行某個動作。

  • 命令編排 Orchestration:集中協調,由一個協調器告訴參與的微服務哪個本地事務需要執行。

優點

  • 為高可伸縮或鬆耦合的、事件驅動的微服務架構提供一致性事務。

  • 為使用了不支援 2PC 的非關聯式資料庫的微服務架構提供一致性事務。

缺點

  • 需要處理瞬時故障,並且提供等冪性。

  • 難以偵錯,而且複雜性隨著微服務數量增加而增加。

何時使用 Saga

  • 在使用了事件源的高可伸縮、鬆耦合的微服務中。

  • 在使用了分散式非關聯式資料庫的系統中。

何時不宜使用 Saga

  • 使用關聯式資料庫的低可伸縮性事務型系統。

  • 在服務間存在迴圈依賴的系統中。

可用技術範例

Axon, Eventuate, Narayana

延伸閱讀

Saga分散式事務-Azure設計模式

微服務模式:Sagas

Saga 模式:微服務中的應用程式事務

 

面向前端的後端 (BFF)

在現代商業應用開發,特別是微服務架構中,前後端應用是分離和獨立的服務,它們通過 API 或 GraphQL 連線。如果應用程式還有移動 App 使用者端,那麼 Web 端和移動使用者端使用相同的後端微服務就會出現問題。因為移動使用者端和 Web 使用者端有不同的螢幕尺寸、顯示屏、效能、能耗和網路頻寬,它們的 API 需求不同。

面向前端的後端模式適用於需要為特殊 UI 客製化單獨後端的場景。它還提供了其他優勢,比如作為下游微服務的封裝,從而減少 UI 和下游微服務之間的頻繁通訊。此外,在高安全要求的場景中,BFF 為部署在 DMZ 網路中的下游微服務提供了更高的安全性。

Md Kamaruzzaman 的面向前端的後端

優點

  • 分離 BFF 之間的關注點,使得我們可以為具體的 UI 優化他們。

  • 提供更高的安全性。

  • 減少 UI 和下游微服務之間頻繁的通訊。

缺點

  • BFF 之間程式碼重複。

  • 大量的 BFF 用於其他使用者介面(例如,智慧電視,Web,行動端,PC 桌面版)。

  • 需要仔細的設計和實現,BFF 不應該包含任何業務邏輯,而應只包含特定使用者端邏輯和行為。

何時使用 BFF

  • 如果應用程式有多個含不同 API 需求的 UI。

  • 出於安全需要,UI 和下游微服務之間需要額外的層。

  •  如果在 UI 開發中使用微前端。

何時不宜使用 BFF

  • 如果應用程式雖有多個 UI,但使用的 API 相同。

  • 如果核心微服務不是部署在 DMZ 網路中。

可用技術範例

任何後端框架(Node.js,Spring,Django,Laravel,Flask,Play,…)都能支援。

延伸閱讀

Sam Newman - 面向前端的後端

面向前端的後端模式 - 雲設計模式

微服務模式: API 閘道器模式

 

API 閘道器

在微服務架構中,UI 通常連線多個微服務。如果微服務是細粒度的(FaaS) ,那麼使用者端可能需要連線非常多的微服務,這將變得繁雜和具有挑戰性。此外,這些服務包括它們的 API 還將不斷進化。大型企業還希望能有其他橫切關注點(SSL 終止、身份驗證、授權、節流、紀錄檔記錄等)。

一個解決這些問題的可行方法是使用 API 閘道器。API 閘道器位於使用者端 APP 和後端微服務之間充當 facade,它可以是反向代理,將使用者端請求路由到適當的後端微服務。它還支援將使用者端請求扇出到多個微服務,然後將響應聚合後返回給使用者端。它還支援必要的橫切關注點。

 

Md Kamaruzzaman 的 API 閘道器

優點

  • 在前端和後端服務之間提供鬆耦合。

  • 減少使用者端和微服務之間的呼叫次數。

  • 通過 SSL 終端、身份驗證和授權實現高安全性。

  • 集中管理的橫切關注點,例如,紀錄檔記錄和監視、節流、負載平衡。

缺點

  • 可能導致微服務架構中的單點故障。

  • 額外的網路呼叫帶來的延遲增加。

  • 如果不進行擴充套件,它們很容易成為整個企業應用的瓶頸。

  • 額外的維護和開發費用。

何時使用 API 閘道器

  • 在複雜的微服務架構中,它幾乎是必須的。

  • 在大型企業中,API 閘道器是中心化安全性和橫切關注點的必要工具。

何時不宜使用 API 閘道器

  • 在安全和集中管理不是最優先要素的私人專案或小公司中。

  • 如果微服務的數量相當少。

可用技術範例

Amazon API 閘道器, Azure API 管理, Apigee, Kong, WSO2 API 管理器

延伸閱讀

微服務模式: API 閘道器模式

API 閘道器-Azure 架構中心

 

Strangler

如果想在執行中的專案中使用微服務架構,我們需要將遺留的或現有的單體應用遷移到微服務。將現有的大型線上單體應用程式遷移到微服務是相當有挑戰性的,因為這可能破壞應用程式的可用性。

一個解決方案是使用 Strangler 模式。Strangler 模式意味著通過使用新的微服務逐步替換特定功能,將單體應用程式增量地遷移到微服務架構。此外,新功能只在微服務中新增,而不再新增到遺留的單體應用中。然後設定一個 Facade (API 閘道器)來路由遺留單體應用和微服務間的請求。當某個功能從單體應用遷移到微服務,Facade 就會攔截使用者端請求並路由到新的微服務。一旦遷移了所有的功能,遺留單體應用程式就會被「扼殺(Strangler)」,即退役。

 

Md Kamaruzzaman 的 Strangler

優點

  • 安全的遷移單體應用程式到微服務。

  • 可以並行地遷移已有功能和開發新功能。

  • 遷移過程可以更好把控節奏。

缺點

  • 在現有的單體應用服務和新的微服務之間共用資料儲存變得具有挑戰性。

  • 新增 Facade (API 閘道器)將增加系統延遲。

  • 端到端測試變得困難。

何時使用 Strangler

  • 將大型後端單體應用程式的增量遷移到微服務。

何時不宜使用 Strangler

  • 如果後端單體應用很小,那麼全量替換會更好。

  • 如果無法攔截使用者端對遺留的單體應用程式的請求。

可用技術範例

API 閘道器後端應用框架。

延伸閱讀

bliki: StranglerFig 應用程式

Strangler 模式 - 雲設計模式

微服務模式:Strangler 應用程式

 

斷路器

在微服務架構中,微服務通過同步呼叫其他服務來滿足業務需求。服務呼叫會由於瞬時故障(網路連線緩慢、超時或暫時不可用) 導致失敗,這種情況重試可以解決問題。然而,如果出現了嚴重問題(微服務完全失敗),那麼微服務將長時間不可用,這時重試沒有意義且浪費寶貴的資源(執行緒被阻塞,CPU 週期被浪費)。此外,一個服務的故障還會引發整個應用系統的級聯故障。這時快速失敗是一種更好的方法。

在這種情況,可以使用斷路器模式挽救。一個微服務通過代理請求另一個微服務,其工作原理類似於電氣斷路器,代理通過統計最近發生的故障數量,並使用它來決定是繼續請求還是簡單的直接返回異常。

Md Kamaruzzaman 的斷路器

斷路器可以有以下三種狀態:

 

  • 關閉:斷路器將請求路由到微服務,並統計給定時段內的故障數量,如果超過閾值,它就會觸發並進入開啟狀態。

  • 開啟:來自微服務的請求會快速失敗並返回異常。在超時後,斷路器進入半開啟狀態。

  • 半開:只有有限數量的微服務請求被允許通過並進行呼叫。如果這些請求成功,斷路器將進入閉合狀態。如果任何請求失敗,斷路器則會進入開啟狀態。

優點

  • 提高微服務架構的容錯性和彈性。

  • 阻止引發其他微服務的級聯故障。

缺點

  • 需要複雜的例外處理。

  • 紀錄檔和監控。

  • 應該支援人工復位。

何時使用斷路器

  • 在微服務間使用同步通訊的緊耦合的微服務架構中。

  • 如果微服務依賴多個其他微服務。

何時不宜使用斷路器

  • 鬆耦合、事件驅動的微服務架構。

  • 如果微服務不依賴於其他微服務。

可用技術範例

API 閘道器,服務網格,各種斷路器庫(Hystrix, Reselience4J, Polly)。

延伸閱讀

bliki:斷路器

斷路器模式 - 雲設計模式

微型服務模式:斷路器

 

外部化設定

每個業務應用都有許多用於各種基礎設施的設定引數(例如,資料庫、網路、連線的服務地址、憑據、證書路徑)。此外在企業應用程式通常部署在各種執行環境(Local、 Dev、 Prod)中,實現這些的一個方法是通過內部設定。這是一個致命糟糕實踐,它會導致嚴重的安全風險,因為生產憑證很容易遭到破壞。此外,設定引數的任何更改都需要重新構建應用程式,這在在微服務架構中會更加嚴峻,因為我們可能擁有數百個服務。

更好的方法是將所有設定外部化,使得構建過程與執行環境分離,生產的組態檔只在執行時或通過環境變數使用,從而最小化了安全風險。

優點

  • 生產設定不屬於程式碼庫,因而最小化了安全漏洞。

  • 修改設定引數不需要重新構建應用程式。

缺點

  • 我們需要選擇一個支援外部化設定的框架。

何時使用外部化設定

  • 任何重要的生產應用程式都必須使用外部化設定。

何時不宜使用外部化設定

  • 在驗證概念的開發中。

可用技術範例

幾乎所有企業級的現代框架都支援外部化設定。

延伸閱讀

微服務模式:外部化設定

一次構建,到處執行:外部化你的設定

 

消費端驅動的契約測試

在微服務架構中,通常有許多有不同團隊開發的微服務。這些微型服務協同工作來滿足業務需求(例如,客戶請求),並相互進行同步或非同步通訊。消費端微服務的整合測試具有挑戰性,通常用 TestDouble 以獲得更快、更低成本的測試執行。但是 TestDouble 通常並不能代表真正的微服務提供者,而且如果微服務提供者更改了它的 API 或 訊息,那麼 TestDouble 將無法確認這些。另一種選擇是進行端到端測試,儘管它在生產之前是強制性的,但卻是脆弱的、緩慢的、昂貴的且不能替代整合測試(Test Pyramid)。

在這方面消費端驅動的契約測試可以幫助我們。在這裡,負責消費端微服務的團隊針對特定的伺服器端微服務,編寫一套包含了其請求和預期響應(同步)或訊息(非同步)的測試套件,這些測試套件稱為顯式的約定。對於微服務伺服器端,將其消費端所有約定的測試套件都新增到其自動化測試中。當特定伺服器端微服務的自動化測試執行時,它將一起執行自己的測試和約定的測試並進行驗證。通過這種方式,契約測試可以自動的幫助維護微服務通訊的完整性。

優點

  • 如果提供程式意外更改 API 或訊息,可以被快速的自動發現。

  • 更少意外、更健壯,特別是包含大量微服務的企業應用程式。

  • 改善團隊自主性。

缺點

  •  需要額外的工作來開發和整合微服務伺服器端的契約測試,因為他們可能使用完全不同的測試工具。

  • 如果契約測試與真實服務情況不匹配,將可能導致生產故障。

何時使用需求驅動的契約測試

  • 在大型企業業務應用程式中,通常由不同的團隊開發不同服務。

何時不宜使用消費端驅動的契約測試

  • 所有微服務由同一團隊負責開發的小型簡單的應用程式。

  • 如果伺服器端微服務是相對穩定的,並且不處在活躍的開發狀態。

可用技術範例

Pact, Postman, Spring Cloud Contract

延伸閱讀

需求驅動契約:一種服務演進模式

微服務模式:服務整合契約測試

什麼是消費端驅動的契約測試?

 

----------------------------------
大家好,我是流水,一個資深的IT從業人員和架構師. 非常高興您能搜尋到,並看到這篇文章,希望這篇文章的內容能給您帶來新的知識和幫助。

也歡迎掃描以下的二維條碼或微信搜尋 「superxtech」,關注我的微信公眾號 , 我會把更多更好的IT領域技術知識帶給您!

----------------------------------