單體應用程式通常具有一個單一的關係型資料庫。使用關係型資料庫的一個主要優點是您的應用程式可以使用 ACID 事務。很不幸的是,當我們轉向微服務架構時,數據訪問將變得非常複雜。這是因為每個微服務所擁有的數據對當前微服務來說是私有的,只能通過其提供的 API 進行訪問。封裝數據可確保微服務松耦合,獨立演... ...
https://github.com/oopsguy/microservices-from-design-to-deployment-chinese
譯者:http://oopsguy.com
本書主要介紹如何使用微服務構建應用程式,這是本書的第五章。第一章介紹了微服務架構模式,討論了使用微服務的優點與缺點。第二和第三章描述了微服務架構內通信方式的對比。第四章探討了與服務發現相關的內容。在本章中,我們稍微做了點調整,研究微服務架構中出現的分散式數據管理問題。
5.1、微服務和分散式數據管理問題
單體應用程式通常具有一個單一的關係型資料庫。使用關係型資料庫的一個主要優點是您的應用程式可以使用 ACID 事務,這些事務提供了以下重要保障:
- 原子性(Atomicity) - 所作出的改變是原子操作,不可分割
- 一致性(Consistency) - 資料庫的狀態始終保持一致
- 隔離性(Isolation) - 即使事務併發執行,但他們看起來更像是串列執行
- 永久性(Durable) - 一旦事務提交,它將不可撤銷
因此,您的應用程式可以很容易地開始事務、更改(插入、更新和刪除)多個行,並提交事務。
使用關係資料庫的另一大好處是它提供了 SQL,這是一種豐富、聲明式和標準化的查詢語言。您可以輕鬆地編寫一個查詢來組合來自多個表的數據,之後,RDBMS 查詢計劃程式將確定執行查詢的最佳方式。您不必擔心如何訪問資料庫等底層細節。因為您所有的應用程式數據都存放在同個資料庫中,因此很容易查詢。
很不幸的是,當我們轉向微服務架構時,數據訪問將變得非常複雜。這是因為每個微服務所擁有的數據對當前微服務來說是私有的,只能通過其提供的 API 進行訪問。封裝數據可確保微服務松耦合,獨立演進。如果多個服務訪問相同的數據,模式(schema)更新需要對所有服務進行耗時、協調的更新。
更糟糕的是,不同的微服務經常使用不同類型的資料庫。現代應用程式存儲和處理著各種數據,而關係型資料庫並不總是最佳選擇。在某些場景,特定的 NoSQL 資料庫可能具有更方便的數據模型,提供了更好的性能和可擴展性。例如,存儲和查詢文本的服務使用文本搜索引擎(如 Elasticsearch)是合理的。類似地,存儲社交圖數據的服務應該可以使用圖資料庫,例如 Neo4j。因此,基於微服務的應用程式通常混合使用 SQL 和 NoSQL 資料庫,即所謂的混合持久化(polyglot persistence)方式。
一個分區的數據存儲混合持久化架構具有許多優點,包括了松耦合的服務以及更好的性能與可擴展性。然而,它也引入了一些分散式數據管理方面的挑戰。
第一個挑戰是如何實現維護多個服務之間的業務事務一致性。要瞭解此問題,讓我們先來看一個線上 B2B 商店的示例。Customer Service (顧客服務)維護客戶相關的信息,包括信用額度。Order Service (訂單)負責管理訂單,並且必須驗證新訂單,不得超過客戶的信用額度。在此應用程式的單體版本中,Order Service 可以簡單地使用 ACID 交易來檢查可用信用額度並創建訂單。
相比之下,在微服務架構中,ORDER (訂單)和 CUSTOMER (顧客)表對其各自的服務都是私有的,如圖 5-1 所示:
Order Service 無法直接訪問 CUSTOMER 表。它只能使用客戶服務提供的 API。訂單服務可能使用了分散式事務,也稱為兩階段提交(2PC)。然而,2PC 在現代應用中通常是不可行的。CAP 定理要求您在可用性與 ACID 式一致性之間做出選擇,可用性通常是更好的選擇。此外,許多現代技術,如大多數 NoSQL 資料庫,都不支持 2PC。維護服務和資料庫之間的數據一致性至關重要,因此我們需要另一套解決方案。
第二個挑戰是如何實現從多個服務中檢索數據。例如,我們假設應用程式需要顯示一個顧客和他最近的訂單。如果 Order Service 提供了用於檢索客戶訂單的 API,那麼您可以使用應用程式端連接以檢索數據。應用程式從 Customer Service 中檢索客戶,並從 Order Service 中檢索客戶的訂單。但是,假設 Order Service 僅支持通過主鍵查找訂單(也許它使用了僅支持基於主鍵檢索的 NoSQL 資料庫)。在這種情況下,沒有有效的方法來檢索所需的數據。
5.2、事件驅動架構
許多應用使用了事件驅動架構作為解決方案。在此架構中,微服務在發生某些重要事件時發佈一個事件,例如更新業務實體時。其他微服務訂閱了這些事件,當微服務接收到一個事件時,它可以更新自己的業務實體,這可能導致更多的事件被髮布。
您可以使用事件實現跨多服務的業務事務。一個事務由一系列的步驟組成。每個步驟包括了微服務更新業務實體和發佈事件所觸發的下一步驟。下圖依次展示瞭如何在創建訂單時使用事件驅動方法來檢查可用信用額度。
微服務通過 Message Broker (消息代理)進行交換事件:
- Order Service (訂單服務)創建一個狀態為 NEW 的訂單,併發布一個 Order
Created (訂單創建)事件。
- Customer Service (客戶服務)消費了 Order Created 事件,為訂單預留信用額度,併發布 Credit Reserved 事件。
- Order Service 消費了 Credit Reserved (信用預留)事件並將訂單的狀態更改為 OPEN。
更複雜的場景可能會涉及額外的步驟,例如在檢查客戶信用的同時保留庫存。
假設(a)每個服務原子地更新資料庫併發布事件,稍後再更新,(b)Message Broker 保證事件至少被傳送一次,您可以實現跨多服務的業務事務。需要註意的是,這些並不是 ACID 事務。它們只提供了更弱的保證,如最終一致性。該事務模型稱為 BASE 模型。
您還可以使用事件來維護多個微服務預先加入所擁有的數據的物化視圖(materialized view)。維護視圖的服務訂閱了相關事件並更新視圖。圖 5-5 展示了 Customer Order View Updater Service (客戶訂單視圖更新服務)根據 Customer
Service 和 Order Service 發佈的事件更新 Customer Order View (客戶訂單服務)。
當 Customer Order View Updater Service 接收到 Customer 或 Order 事件時,它會更新 Customer Order View 數據存儲。您可以使用如 MongoDB 之類的文檔資料庫實現 Customer Order
View,併為每個 Customer 存儲一個文檔。Customer Order View Query Service (客戶訂單視圖查詢服務)通過查詢 Customer Order View 數據存儲來處理獲取一位客戶和最近的訂單的請求。
事件驅動的架構有幾個優點與缺點。它能夠實現跨越多服務並提供最終一致性事務。另一個好處是它還使得應用程式能夠維護物化視圖。
一個缺點是其編程模型比使用 ACID 事務更加複雜。通常,您必須實現補償事務以從應用程式級別的故障中恢復。例如,如果信用檢查失敗,您必須取消訂單。此外,應用程式必須處理不一致的數據。因為未提交的事務所做的更改是可見的。如果從未更新的物化視圖中讀取,應用程式依然可以看到不一致性。另一個缺點是訂閱者必須要檢測和忽略重覆的事件。
5.3、實現原子性
在事件驅動架構中,同樣存在著原子更新資料庫和發佈事件相關問題。例如,Order Service 必須在 ORDER 表中插入一行數據,併發布 Order Created 事件。這兩個操作必須原子完成。如果在更新資料庫後但在發佈事件之前發生服務崩潰,系統將出現不一致性。確保原子性的標準方法是使用涉及到資料庫和 Message Broker 的分散式事務。然而,由於上述原因,如 CAP 定理,這並不是我們想做的。
5.4、使用本地事務發佈事件
實現原子性的一種方式是應用程式使用僅涉及本地事務的多步驟過程來發佈事件。訣竅在於存儲業務實體狀態的資料庫中有一個用作消息隊列的 EVENT 表。應用程式開啟一個(本地)資料庫事務,更新業務實體狀態,將事件插入到 EVENT 表中,之後提交事務。一個單獨的應用程式線程或進程查詢 EVENT 表,將事件發佈到 Message Broker,然後使用本地事務將事件標記為已發佈。設計如圖 5-6 所示。
Order Service 將一行記錄插入到 ORDER 表中,並將一個 Order Created 事件插入到 EVENT 表中。Event Publisher(事件發佈者)線程或進程從 EVENT 表中查詢未發佈的事件,之後發佈這些事件,最後更新 EVENT 表以將事件標記為已發佈。
這種方法有好有壞。好處是它保證了被髮布的事件每次更新都不依賴於 2PC。此外,應用程式發佈業務級事件,這些事件可以消除推斷的需要。這種方法的缺點是它很容易出錯,因為開發人員必須要記得發佈事件。這種方法的局限性在於,由於其有限的事務和查詢功能,在使用某些 NoSQL 資料庫時,實現起來將是一大挑戰。
該方法通過讓應用程式使用本地事務更新狀態和發佈事件來消除對 2PC 的依賴。現在我們來看一下通過應用程式簡單地更新狀態來實現原子性的方法。
5.5、挖掘資料庫事務日誌
不依靠 2PC 來實現原子性的另一種方式是使用線程或進程發佈事件,該線程或進程對資料庫的事務或者提交日誌進行挖掘。當應用程式更新資料庫時,更改信息被記錄到資料庫的事務日誌中。Transaction Log Miner 線程或進程讀取事務日誌並向 Message Broker 發佈事件。設計如圖 5-7 所示。
使用這種方法的一個示例是 LinkedIn Databus 開源項目。Databus 挖掘 Oracle 事務日誌併發布與更改相對應的事件。LinkedIn 使用 Databus 保持與記錄系統一致的各種派生數據存儲。
另一個例子是 AWS DynamoDB 中的流機制,它是一個托管的 NoSQL 資料庫。DynamoDB 流包含了在過去 24 小時內對 DynamoDB 表中的項進行的更改(創建、更新和刪除操作),其按時間順序排列。應用程式可以從流中讀取這些更改,比如,將其作為事件發佈。
事務日誌挖掘有各種好處與壞處。一個好處是它能保證被髮布的事件每次更新都不依賴於 2PC。事務日誌挖掘還可以通過將事件發佈與應用程式的業務邏輯分離來簡化應用程式。一個主要的缺點是事務日誌的格式對於每個資料庫來說都是專有的,甚至在資料庫版本之間格式就發生了改變。而且,記錄於事務日誌中的低級別更新可能難以對高級業務事件進行逆向工程。
事務日誌挖掘消除了應用程式在做一件事時對 2PC 的依賴:更新資料庫。現在我們來看看另一種可以消除更新並僅依賴於事件的不同方式。
5.6、使用事件溯源
事件溯源通過使用完全不同的、不間斷的方式來持久化業務實體,實現無 2PC 原子性。應用程式不存儲實體的當前狀態,而是存儲一系列狀態改變事件。該應用程式通過回放事件來重建實體的當前狀態。無論業務實體的狀態何時發生變化,其都會將新事件追加到事件列表中。由於保存事件是一個單一操作,因此具有原子性。
要瞭解事件溯源的工作原理,以 Order(訂單)實體為例。在傳統方式中,每個訂單都與 ORDER 表中的某行記錄相映射,也可以映射到例如 ORDER_LINE_ITEM 表中的記錄。
但當使用事件溯源時,Order Service 將以狀態更改事件的形式存儲 Order:Created(創建)、Approved(批准)、Shipped(發貨)、Cancelled(取消)。每個事件包含足夠的數據來重建 Order 的狀態。
事件被持久化在事件存儲中,事件存儲是一個事件資料庫。該存儲有一個用於添加和檢索實體事件的 API。事件存儲還與我們之前描述的架構中的 Message Broker 類似。它提供了一個 API,使得服務能夠訂閱事件。事件存儲向所有感興趣的訂閱者派發所有事件。可以說事件存儲是事件驅動微服務架構的支柱。
事件溯源有幾個好處。它解決了實現事件驅動架構的關鍵問題之一,可以在狀態發生變化時可靠地發佈事件。因此,它解決了微服務架構中的數據一致性問題。此外,由於它持久化的是事件,而不是領域對象,所以它主要避免了對象關係阻抗失配問題。事件溯源還提供了對業務實體所做更改的 100% 可靠的審計日誌,可以實現在任何時間點對實體進行時間查詢以確定狀態。事件溯源的另一個主要好處是您的業務邏輯包括松耦合的交換事件業務實體,這使得從單體應用程式遷移到微服務架構將變得更加容易。
事件溯源同樣有缺點。這是一種不同而陌生的編程風格,因此存在學習曲線。事件存儲僅支持通過主鍵查找業務實體。您必須使用命令查詢責任分離(CQRS)來實現查詢。因此,應用程式必須處理最終一致的數據。
5.7、總結
在微服務架構中,每個微服務都有自己私有的數據存儲。不同的微服務可能會使用不同的 SQL 或者 NoSQL 資料庫。雖然這種資料庫架構具有明顯的優勢,但它創造了一些分散式數據管理挑戰。第一個挑戰是如何實現維護多個服務間的業務事務一致性。第二個挑戰是如何實現從多個服務中檢索數據。
大部分應用使用的解決方案是事件驅動架構。實現事件驅動架構的一個挑戰是如何以原子的方式更新狀態以及如何發佈事件。有幾種方法可以實現這點,包括了將資料庫作為消息隊列、事務日誌挖掘和事件溯源。
微服務實戰:NGINX 與存儲優化
by Floyd Smith
基於微服務的存儲方法涉及大數量和各種數據存儲,訪問和更新數據將變得更加複雜,DevOps 在維護數據一致性方面面臨著更大的挑戰。NGINX 為這種數據管理提供了重要支持,主要有三個方面:
- 數據緩存與微緩存(microcaching) - 使用 NGINX 緩存靜態文件和微緩存應用程式生成的內容可減輕應用程式的負載、提高性能並減少問題的發生。
- 數據存儲的靈活性與可擴展性 - 一旦將 NGINX 作為反向代理伺服器,您的應用程式在創建、調整大小、運行和調整數據存儲伺服器的大小時可獲得很大的靈活性,以滿足不斷變化的需求 - 每個服務都擁有自己的數據存儲是很重要的。
- 服務監控與管理,包括數據服務 - 隨著數據伺服器數量的增加,支持複雜操作和具有監控和管理工具顯得非常重要。NGINX Plus 內置了這些工具和應用程式性能管理合作伙伴的介面,如 Data Dog、Dynatrace 和 New Relic。
微服務相關的數據管理示例可在 NGINX 微服務參考架構的三大模型中找到,其為您設計決策和實施提供了起點。
此系列全部譯文
https://github.com/oopsguy/microservices-from-design-to-deployment-chinese