微服務的數據考慮

Azure DevOps

本文說明在微服務架構中管理數據的考慮。 因為每個微服務都會管理自己的數據,因此數據完整性和數據一致性是一項重大挑戰。

微服務的基本原則是每個服務都會管理自己的數據。 兩個服務不應該共用數據存放區。 相反地,每個服務都會負責自己的私人數據存放區,而其他服務則無法直接存取。

此規則的原因是避免服務之間無意的結合,這可能會導致服務共用相同的基礎數據架構。 如果數據架構有所變更,則必須跨依賴該資料庫的每個服務協調變更。 藉由隔離每個服務的數據存放區,我們可以限制變更的範圍,並保留真正獨立部署的靈活度。 另一個原因是每個微服務可能有自己的數據模型、查詢或讀取/寫入模式。 使用共用數據存放區可限制每個小組針對其特定服務優化數據記憶體的能力。

Diagram of a wrong approach to CQRS

這種方法自然會導致 polyglot 持續性 — 在單一應用程式中使用多個資料儲存技術。 一項服務可能需要檔資料庫的架構讀取功能。 另一個可能需要 RDBMS 所提供的引用完整性。 每個小組都能自由地為其服務做出最佳選擇。

注意

服務可以共用相同的實體資料庫伺服器。 當服務共用相同的架構,或讀取和寫入相同的資料庫數據表集時,就會發生此問題。

挑戰

這種分散式管理數據的方法會產生一些挑戰。 首先,數據存放區可能會有備援性,而相同的數據專案會出現在多個位置。 例如,數據可能會儲存為交易的一部分,然後儲存在其他地方進行分析、報告或封存。 重複或分割的數據可能會導致數據完整性和一致性的問題。 當數據關聯性跨越多個服務時,您無法使用傳統的數據管理技術來強制執行關聯性。

傳統的數據模型化會使用「一個事實在一個地方」的規則。每個實體在架構中只出現一次。 其他實體可能會保存對它的參考,但不重複它。 傳統方法的明顯優點是,更新是在單一位置進行,可避免數據一致性問題。 在微服務架構中,您必須考慮如何在服務之間傳播更新,以及如何在數據出現在多個位置而不具有強式一致性時管理最終一致性。

管理數據的方法

在所有情況下都沒有任何正確的單一方法,但以下是在微服務架構中管理數據的一些一般指導方針。

  • 可能的話,請採用最終一致性。 了解系統中需要強式一致性或 ACID 交易的位置,以及可接受最終一致性的位置。

  • 當您需要強式一致性保證時,一個服務可能會代表特定實體的真相來源,而該實體會透過 API 公開。 其他服務可能會保存自己的數據複本,或數據子集,最終與主要數據一致,但不會被視為事實來源。 例如,假設具有客戶訂單服務和建議服務的電子商務系統。 建議服務可能會接聽訂單服務的事件,但如果客戶要求退款,則為訂單服務,而非具有完整交易歷程記錄的建議服務。

  • 對於交易,請使用排程器代理程序監督員補償交易等模式,讓數據在數個服務之間保持一致。 您可能需要儲存額外的數據片段,以擷取跨越多個服務的工作單位狀態,以避免多個服務之間的部分失敗。 例如,在多步驟交易進行時,將工作專案保留在長期佇列上。

  • 只儲存服務所需的數據。 服務可能只需要網域實體的相關信息子集。 例如,在出貨限定內容中,我們需要知道哪些客戶與特定傳遞相關聯。 但我們不需要客戶的帳單位址, 這是由帳戶限定內容所管理。 仔細思考網域,以及使用 DDD 方法,可以在這裡有所説明。

  • 請考慮您的服務是否一致且鬆散結合。 如果兩個服務會持續彼此交換資訊,導致閒聊的 API,您可能需要藉由合併兩個服務或重構其功能來重新繪製服務界限。

  • 使用事件驅動架構樣式。 在此架構樣式中,服務會在公用模型或實體變更時發佈事件。 感興趣的服務可以訂閱這些事件。 例如,另一個服務可以使用 事件來建構更適合查詢之數據的具體化檢視。

  • 擁有事件的服務應該發佈架構,可用來自動串行化和還原串行化事件,以避免發行者和訂閱者之間緊密結合。 請考慮 JSON 架構或架構,例如 Microsoft Bond、Protobuf 或 Avro。

  • 大規模事件可能會成為系統上的瓶頸,因此請考慮使用匯總或批處理來減少總負載。

範例:選擇無人機遞送應用程式的數據存放區

本系列中的前一篇文章將無人機遞送服務討論為執行中的範例。 您可以在這裡深入瞭解案例和對應的參考實作。 此範例適用於飛機和航空航天產業。

為了回顧,此應用程式會定義數個微服務,以排程無人機的傳遞。 當使用者排程新的傳遞時,用戶端要求會包含傳遞的相關信息,例如取貨和下車位置,以及套件的相關信息,例如大小和重量。 這項資訊會定義工作單位。

各種後端服務會關心要求中資訊的不同部分,而且也有不同的讀取和寫入配置檔。

Diagram of data considerations

遞送服務

傳遞服務會儲存目前排程或進行中之每個傳遞的相關信息。 它會接聽無人機的事件,並追蹤正在進行中的交付狀態。 它也會傳送傳遞狀態更新的網域事件。

預期使用者會在等候其套件時,經常檢查傳遞的狀態。 因此,傳遞服務需要一個數據存放區,強調長期記憶體的輸送量(讀取和寫入)。 此外,傳遞服務不會執行任何複雜的查詢或分析,只會擷取指定傳遞的最新狀態。 傳遞服務小組選擇 Azure Cache for Redis 以達到高讀寫效能。 儲存在 Redis 中的資訊相對較短。 傳遞完成後,傳遞記錄服務就是記錄系統。

遞送記錄服務

傳遞歷程記錄服務會接聽傳遞服務中的傳遞狀態事件。 它會將此資料儲存在長期記憶體中。 此歷程記錄數據的使用案例有兩個不同的使用案例,其數據儲存需求不同。

第一個案例是匯總數據以供數據分析之用,以便將業務優化或改善服務品質。 請注意,傳遞歷程記錄服務不會執行數據的實際分析。 它只負責擷取和儲存。 在此案例中,記憶體必須針對大量數據進行數據分析優化,並使用架構讀取方法來容納各種數據源。 Azure Data Lake Store 非常適合此案例。 Data Lake Store 是與 Hadoop 分散式文件系統 (HDFS) 相容的 Apache Hadoop 文件系統,且已針對數據分析案例的效能進行調整。

另一個案例是讓用戶在傳遞完成後查閱傳遞的歷程記錄。 Azure Data Lake 未針對此案例進行優化。 為了達到最佳效能,Microsoft 建議將時間序列數據儲存在依日期分割的資料夾中。 (請參閱 調整 Azure Data Lake Store 以達到效能)。 不過,該結構不是依標識碼查閱個別記錄的最佳結構。 除非您也知道時間戳,否則依標識元查閱需要掃描整個集合。 因此,「傳遞歷程記錄」服務也會將歷程記錄數據的子集儲存在 Azure Cosmos DB 中,以便更快速地查閱。 記錄不需要無限期地留在 Azure Cosmos DB 中。 舊交付可以封存 - 例如,在一個月後。 這可以藉由執行偶爾的批處理來完成。 封存較舊的數據可以減少 Cosmos DB 的成本,同時仍保留可從 Data Lake 進行歷程記錄報告的數據。

包裹服務

封裝服務會儲存所有套件的相關信息。 套件的記憶體需求如下:

  • 長期儲存體。
  • 能夠處理大量套件,需要高寫入輸送量。
  • 依套件標識碼支援簡單的查詢。 沒有複雜聯結或引用完整性的需求。

由於套件數據不是關係型資料庫,因此文件導向資料庫是適當的,而且 Azure Cosmos DB 可以使用分區化集合達到高輸送量。 在套件服務上運作的小組熟悉 MEAN 堆疊 (MongoDB、Express.js、AngularJS 和 Node.js),因此他們會選取適用於 Azure Cosmos DB 的 MongoDB API 。 這可讓他們利用 MongoDB 的現有體驗,同時獲得 Azure Cosmos DB 的優點,這是受控 Azure 服務。

下一步

瞭解可協助減輕微服務架構中一些常見挑戰的設計模式。