Chris Richardson 微服務系列翻譯全7篇連接:html
原文連接:Event-Driven Data Management for Microservices
nginx
單體應用通常只有一個關係型數據庫,這樣的好處是能夠實現 ACID 保證:git
所以,應用能夠簡單的開始事務,更改(增刪改)多行數據,而後提交事務。github
使用關係型數據庫另外一好處是支持 SQL。SQL 是一種豐富的、聲明式的標準查詢語言,用戶能簡易的關聯查詢多個表中的數據,而後RDBMS 查詢調度器會執行最優的查詢方式,用戶沒必要關係底層的細節。全部的數據在一個數據庫中也方便查詢。數據庫
然而微服務架構中數據訪問變的複雜,由於每一個微服務都擁有獨立的數據庫,僅能經過 API 來訪問。數據封裝保證了微服務的鬆耦合,各個服務能夠獨立其餘服務演進。而若是多個服務訪問一樣的數據,架構更新會更耗費時間,也須要更多的服務協調。編程
不一樣服務可能使用不一樣類型的數據庫,現代應用存儲和處理各類各樣的數據,關係數據庫並不老是最好的選擇。一些場景下,一種特殊的 NoSQL 能提供更方便的數據模型、更好的性能和可擴展性。例如:使用 ElasticSearch 這樣的搜索引擎來進行文本的存儲和查詢;使用 Neo4j 這樣的圖譜數據庫來存儲社交圖譜數據。所以,微服務應用會混合使用 SQL 和 NoSQL 數據庫,即多態型數據持久方案。這也帶來了一些挑戰:架構
1)如何跨多個服務實現事務,維護數據的一致性。咱們以 B2B 商店爲例:客戶服務維護用戶信用額度等相關的信息,訂單服務管理訂單並確保新訂單沒有超過用戶的信用額度。單體應用中,訂單服務可使用 ACID 事務來覈對用戶信用額度並新建訂單。而在微服務架構中, order 和 customer 表是各個服務私有的:併發
訂單服務沒法直接訪問 customer 表,只能經過客戶服務的 API。訂單服務可能使用分佈式事務,也被稱爲兩階段提交(2PC),然而 2PC 在現代應用一般不是很好的選擇。CAP 定理須要用戶在可用性和 ACID 的一致性中二選一,一般可用性是更好的選擇。此外,不少NoSQL 並不支持 2PC,維護服務和數據庫中數據的一致性是很重要的,所以咱們能夠選擇另外一種方案。分佈式
2)另外一個挑戰是如何檢索多個服務中的數據,例如應用須要顯示一位客戶和他最近的訂單,若是訂單服務提供了用戶訂單的查詢 API,那麼能夠在應用端獲取該數據,應用端經過客戶服務檢索客戶,再經過訂單服務檢索該客戶的訂單。假設訂單服務只支持經過主鍵來查詢訂單,此時就沒有合適的方法來檢索所需數據了。ide
對於許多應用,解決方案就是事件驅動的架構:服務在業務發生時(例如更新一條記錄信息)會發佈一個事件,其餘微服務訂閱該事件,當某一微服務接收到事件就會更新本身的業務記錄,而後其餘更多的事件會被髮布。下圖展現瞭如何使用事件驅動的方式在建立訂單時檢查可用信用,微服務間經過 MQ 來交換事件:
1)訂單服務建立狀態爲 NEW 的訂單,而後發佈『訂單建立』的事件
2)客戶服務獲取『訂單建立』事件,爲此訂單保留信用,發佈『信用保留』事件
3)訂單服務獲取『信用保留』事件,將訂單狀態修改成 OPEN
更爲複雜的場景會涉及更多的步驟,好比覈對用戶信用時預留庫存。
基於(a)每一個服務原子性的更新數據庫併發布事件;(b)MQ 確保事件至少交付一次,那麼就能夠實現跨服務的業務邏輯了。這並不是 ACID 事務,他提供更弱一點的最終一致性。這種事務模型可稱爲 BASE model。
也可使用事件維護關聯多個微服務的物化視圖。維護此視圖的服務訂閱相關事件並更新視圖,例如:用戶訂單視圖經過訂閱訂單事件和用戶事件來進行更新:
當用戶訂單服務收到用戶服務或訂單服務的事件時,會更新視圖。可使用相似 MongoDB 的文檔數據庫爲每一個用戶存儲一份用戶訂單的文檔。
事件驅動架構的優勢:
不足之處:
事件驅動架構還存在一個問題:以原子粒度更新 DB 與發佈事件。例如:訂單服務在訂單表中 insert 一行記錄,而後發佈『訂單建立』的事件,這兩個操做須要是原子性的,不然,更新 DB 後,發佈事件前服務崩潰了,系統將存在不一致。確保原子性的方法是使用 分佈式事務 調用 DB 和 MQ。然而因爲 CAP 理論,咱們是想避免這麼作。
應用發佈事件並保證原子性的方法之一就是:多步驟本地事務方法。技巧是 DB 中有一張 EVENT 表(模擬消息隊列),存儲業務數據的狀態。首先啓動一個本地數據庫事務,更新業務數據記錄並往 EVENT 表中插入一條數據,最後提交事務。一個單獨的線程會輪詢 EVENT 表,將查詢結果往 MQ 中發送事件消息,而後使用本地事務標註事件狀態爲已發佈。以下圖所示:
訂單服務首先往 ORDER 表中 insert 一行記錄,而後在 EVENT 表插入類型爲 Order Created 的事件(狀態爲 NEW )。事件發佈線程或進程輪詢 EVENT 表中未發佈的事件,發佈事件而後更新 EVENT 表事件狀態爲已發佈。
這種方法的優勢:
不足:
另外一種不須要兩階段提交就能實現原子性的方法是:分析數據庫事務日誌或提交日誌來發布事件。應用更新 DB時,DB的事務日誌會記錄這些變動,事務日誌挖掘線程或進程讀取這些日誌,並將事件發佈到 MQ,以下圖所示:
範例之一就是開源的 LinkedIn Databus 項目,Databus 挖掘 Oracle等數據庫的事務日誌併發布相應的事件,LinkedIn 使用 Databus 來保持各類派生數據存儲和記錄系統的一致。
另外一範例就是 streams mechanism in AWS DynamoDB,AWS DynamoDB 流包括 DynamoDB 表在過去 24 小時內的時序變化,包括新建、更新和刪除操做。應用能讀取這些變動,將其做爲事件發佈。
事務日誌挖掘的優勢:
不足:
事件源經過採用一種大相徑庭的、以事件爲中心的方法來保存業務實體,而不須要 2PC 來實現原子性。這種方法存儲一系列狀態變更的事件,而不是實體的當前狀態。應用經過重放事件來構建實體的當前狀態,每當業務實體的狀態改變,就往事件列表中添加新的事件。因爲保存事件是惟一操做,本質上就是原子性的。
以訂單爲例:傳統方案中,每一個訂單爲 ORDER 表中的一行記錄。使用事件源時,訂單服務存儲致使訂單狀態變化的事件,包括建立、批准、配送、取消。每一個事件由充足的信息來從新構建訂單:
事件被存儲 DB 中,可以使用 API 添加或查找實體的事件。事件存儲相似上文說起的 MQ,提供 API 讓其餘服務訂閱事件,將事件傳達至感興趣的訂閱者。事件存儲是事件驅動的微服務架構的支柱。
事件源的優勢:
不足:
微服務架構中,每一個微服務都有本身的數據存儲,不一樣的微服務可能使用不一樣的 SQL 和 NoSQL 數據庫。這些數據庫架構有不少優點,也帶來了分佈式數據管理的挑戰。第一個挑戰就是如何實現跨服務的業務事務,並保證一致性;第二個挑戰就是如何從多個服務中查詢數據。
對於許多應用,解決方案就是使用事件驅動的架構。事件驅動的架構帶來的挑戰是如何原子化地更新狀態和發佈事件。有幾種方案能夠考慮,包括把數據庫用做消息隊列、事務日誌挖掘和事件源。