https://github.com/oopsguy/microservices-from-design-to-deployment-chinese
譯者:http://oopsguy.comhtml
本書主要介紹如何使用微服務構建應用程序,這是本書的第五章。第一章介紹了微服務架構模式,討論了使用微服務的優勢與缺點。第二和第三章描述了微服務架構內通訊方式的對比。第四章探討了與服務發現相關的內容。在本章中,咱們稍微作了點調整,研究微服務架構中出現的分佈式數據管理問題。nginx
單體應用程序一般具備一個單一的關係型數據庫。使用關係型數據庫的一個主要優勢是您的應用程序可使用 ACID 事務,這些事務提供瞭如下重要保障:git
所以,您的應用程序能夠很容易地開始事務、更改(插入、更新和刪除)多個行,並提交事務。github
使用關係數據庫的另外一大好處是它提供了 SQL,這是一種豐富、聲明式和標準化的查詢語言。您能夠輕鬆地編寫一個查詢來組合來自多個表的數據,以後,RDBMS 查詢計劃程序將肯定執行查詢的最佳方式。您沒必要擔憂如何訪問數據庫等底層細節。由於您全部的應用程序數據都存放在同個數據庫中,所以很容易查詢。數據庫
很不幸的是,當咱們轉向微服務架構時,數據訪問將變得很是複雜。這是由於每一個微服務所擁有的數據對當前微服務來講是私有的,只能經過其提供的 API 進行訪問。封裝數據可確保微服務鬆耦合,獨立演進。若是多個服務訪問相同的數據,模式(schema)更新須要對全部服務進行耗時、協調的更新。編程
更糟糕的是,不一樣的微服務常用不一樣類型的數據庫。現代應用程序存儲和處理着各類數據,而關係型數據庫並不老是最佳選擇。在某些場景,特定的 NoSQL 數據庫可能具備更方便的數據模型,提供了更好的性能和可擴展性。例如,存儲和查詢文本的服務使用文本搜索引擎(如 Elasticsearch)是合理的。相似地,存儲社交圖數據的服務應該可使用圖數據庫,例如 Neo4j。所以,基於微服務的應用程序一般混合使用 SQL 和 NoSQL 數據庫,即所謂的混合持久化(polyglot persistence)方式。api
一個分區的數據存儲混合持久化架構具備許多優勢,包括了鬆耦合的服務以及更好的性能與可擴展性。然而,它也引入了一些分佈式數據管理方面的挑戰。緩存
第一個挑戰是如何實現維護多個服務之間的業務事務一致性。要了解此問題,讓咱們先來看一個在線 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 數據庫)。在這種狀況下,沒有有效的方法來檢索所需的數據。
許多應用使用了事件驅動架構做爲解決方案。在此架構中,微服務在發生某些重要事件時發佈一個事件,例如更新業務實體時。其餘微服務訂閱了這些事件,當微服務接收到一個事件時,它能夠更新本身的業務實體,這可能致使更多的事件被髮布。
您可使用事件實現跨多服務的業務事務。一個事務由一系列的步驟組成。每一個步驟包括了微服務更新業務實體和發佈事件所觸發的下一步驟。下圖依次展現瞭如何在建立訂單時使用事件驅動方法來檢查可用信用額度。
微服務經過 Message Broker (消息代理)進行交換事件:
更復雜的場景可能會涉及額外的步驟,例如在檢查客戶信用的同時保留庫存。
假設(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 事務更加複雜。一般,您必須實現補償事務以從應用程序級別的故障中恢復。例如,若是信用檢查失敗,您必須取消訂單。此外,應用程序必須處理不一致的數據。由於未提交的事務所作的更改是可見的。若是從未更新的物化視圖中讀取,應用程序依然能夠看到不一致性。另外一個缺點是訂閱者必需要檢測和忽略重複的事件。
在事件驅動架構中,一樣存在着原子更新數據庫和發佈事件相關問題。例如,Order Service 必須在 ORDER 表中插入一行數據,併發布 Order Created 事件。這兩個操做必須原子完成。若是在更新數據庫後但在發佈事件以前發生服務崩潰,系統將出現不一致性。確保原子性的標準方法是使用涉及到數據庫和 Message Broker 的分佈式事務。然而,因爲上述緣由,如 CAP 定理,這並非咱們想作的。
實現原子性的一種方式是應用程序使用僅涉及本地事務的多步驟過程來發布事件。訣竅在於存儲業務實體狀態的數據庫中有一個用做消息隊列的 EVENT 表。應用程序開啓一個(本地)數據庫事務,更新業務實體狀態,將事件插入到 EVENT 表中,以後提交事務。一個單獨的應用程序線程或進程查詢 EVENT 表,將事件發佈到 Message Broker,而後使用本地事務將事件標記爲已發佈。設計如圖 5-6 所示。
Order Service 將一行記錄插入到 ORDER 表中,並將一個 Order Created 事件插入到 EVENT 表中。Event Publisher(事件發佈者)線程或進程從 EVENT 表中查詢未發佈的事件,以後發佈這些事件,最後更新 EVENT 表以將事件標記爲已發佈。
這種方法有好有壞。好處是它保證了被髮布的事件每次更新都不依賴於 2PC。此外,應用程序發佈業務級事件,這些事件能夠消除推斷的須要。這種方法的缺點是它很容易出錯,由於開發人員必需要記得發佈事件。這種方法的侷限性在於,因爲其有限的事務和查詢功能,在使用某些 NoSQL 數據庫時,實現起來將是一大挑戰。
該方法經過讓應用程序使用本地事務更新狀態和發佈事件來消除對 2PC 的依賴。如今咱們來看一下經過應用程序簡單地更新狀態來實現原子性的方法。
不依靠 2PC 來實現原子性的另外一種方式是使用線程或進程發佈事件,該線程或進程對數據庫的事務或者提交日誌進行挖掘。當應用程序更新數據庫時,更改信息被記錄到數據庫的事務日誌中。Transaction Log Miner 線程或進程讀取事務日誌並向 Message Broker 發佈事件。設計如圖 5-7 所示。
使用這種方法的一個示例是 LinkedIn Databus 開源項目。Databus 挖掘 Oracle 事務日誌併發布與更改相對應的事件。LinkedIn 使用 Databus 保持與記錄系統一致的各類派生數據存儲。
另外一個例子是 AWS DynamoDB 中的流機制,它是一個託管的 NoSQL 數據庫。DynamoDB 流包含了在過去 24 小時內對 DynamoDB 表中的項進行的更改(建立、更新和刪除操做),其按時間順序排列。應用程序能夠從流中讀取這些更改,好比,將其做爲事件發佈。
事務日誌挖掘有各類好處與壞處。一個好處是它能保證被髮布的事件每次更新都不依賴於 2PC。事務日誌挖掘還能夠經過將事件發佈與應用程序的業務邏輯分離來簡化應用程序。一個主要的缺點是事務日誌的格式對於每一個數據庫來講都是專有的,甚至在數據庫版本之間格式就發生了改變。並且,記錄於事務日誌中的低級別更新可能難以對高級業務事件進行逆向工程。
事務日誌挖掘消除了應用程序在作一件事時對 2PC 的依賴:更新數據庫。如今咱們來看看另外一種能夠消除更新並僅依賴於事件的不一樣方式。
事件溯源經過使用徹底不一樣的、不間斷的方式來持久化業務實體,實現無 2PC 原子性。應用程序不存儲實體的當前狀態,而是存儲一系列狀態改變事件。該應用程序經過回放事件來重建實體的當前狀態。不管業務實體的狀態什麼時候發生變化,其都會將新事件追加到事件列表中。因爲保存事件是一個單一操做,所以具備原子性。
要了解事件溯源的工做原理,以 Order(訂單)實體爲例。在傳統方式中,每一個訂單都與 ORDER 表中的某行記錄相映射,也能夠映射到例如 ORDER_LINE_ITEM 表中的記錄。
但當使用事件溯源時,Order Service 將以狀態更改事件的形式存儲 Order:Created(建立)、Approved(批准)、Shipped(發貨)、Cancelled(取消)。每一個事件包含足夠的數據來重建 Order 的狀態。
事件被持久化在事件存儲中,事件存儲是一個事件數據庫。該存儲有一個用於添加和檢索實體事件的 API。事件存儲還與咱們以前描述的架構中的 Message Broker 相似。它提供了一個 API,使得服務可以訂閱事件。事件存儲向全部感興趣的訂閱者派發全部事件。能夠說事件存儲是事件驅動微服務架構的支柱。
事件溯源有幾個好處。它解決了實現事件驅動架構的關鍵問題之一,能夠在狀態發生變化時可靠地發佈事件。所以,它解決了微服務架構中的數據一致性問題。此外,因爲它持久化的是事件,而不是領域對象,因此它主要避免了對象關係阻抗失配問題。事件溯源還提供了對業務實體所作更改的 100% 可靠的審計日誌,能夠實如今任什麼時候間點對實體進行時間查詢以肯定狀態。事件溯源的另外一個主要好處是您的業務邏輯包括鬆耦合的交換事件業務實體,這使得從單體應用程序遷移到微服務架構將變得更加容易。
事件溯源一樣有缺點。這是一種不一樣而陌生的編程風格,所以存在學習曲線。事件存儲僅支持經過主鍵查找業務實體。您必須使用命令查詢責任分離(CQRS)來實現查詢。所以,應用程序必須處理最終一致的數據。
在微服務架構中,每一個微服務都有本身私有的數據存儲。不一樣的微服務可能會使用不一樣的 SQL 或者 NoSQL 數據庫。雖然這種數據庫架構具備明顯的優點,但它創造了一些分佈式數據管理挑戰。第一個挑戰是如何實現維護多個服務間的業務事務一致性。第二個挑戰是如何實現從多個服務中檢索數據。
大部分應用使用的解決方案是事件驅動架構。實現事件驅動架構的一個挑戰是如何以原子的方式更新狀態以及如何發佈事件。有幾種方法能夠實現這點,包括了將數據庫做爲消息隊列、事務日誌挖掘和事件溯源。
by Floyd Smith
基於微服務的存儲方法涉及大數量和各類數據存儲,訪問和更新數據將變得更加複雜,DevOps 在維護數據一致性方面面臨着更大的挑戰。NGINX 爲這種數據管理提供了重要支持,主要有三個方面:
微服務相關的數據管理示例可在 NGINX 微服務參考架構的三大模型中找到,其爲您設計決策和實施提供了起點。
https://github.com/oopsguy/microservices-from-design-to-deployment-chinese