【Chris Richardson 微服務系列】事件驅動的數據管理

編者的話 |本文來自 Nginx 官方博客,是「Chris Richardson 微服務」系列的第五篇文章。第一篇文章介紹了微服務架構模式,并且討論了使用微服務的優缺點;第二和第三篇描述了微服務架構模塊間通訊的不同方面;第四篇研究了服務發現中的問題。本篇研究微服務架構帶來的分布式數據管理問題。

作者介紹:Chris Richardson,是世界著名的軟件大師,經典技術著作《POJOS IN ACTION》一書的作者,也是 cloudfoundry.com 最初的創始人,Chris Richardson 與 Martin Fowler、Sam Newman、Adrian Cockcroft 等并稱為世界十大軟件架構師。

Chris Richardson 微服務系列全 7 篇:

1. 微服務架構概念解析
2. 構建微服務架構:使用 API Gateway
3. 深入微服務架構的進程間通信
4. 服務發現的可行方案以及實踐案例
5. 微服務的事件驅動數據管理(本篇文章)
6. 選擇微服務部署策略
7. 將單體應用改造為微服務

微服務以及分布式數據管理中存在的問題

單體應用通常使用單個關系型數據庫,由此帶來的好處在于應用能夠使用 ACID 事務,后者提供了重要的操作特性:

  • 原子化:原子粒度的更改
  • 一致性:數據庫的狀態始終保持一致
  • 隔離:并發執行的事務顯示為串行執行
  • 持久:事務一旦提交就不會被撤銷

如此,應用能夠簡單地開始事務、更改(插入、更新和刪除)多行、以及提交事務。

使用關系型數據庫的另一大好處是它支持 SQL。SQL 是一門豐富、可聲明的和標準化的查詢預約。用戶能夠輕松通過查詢將多個表中的數據組合起來,然后 RDBMS 查詢調度器決定執行查詢的最優方法。用戶不必關心底層細節,比如如何訪問數據庫。此外,由于所有的應用數據在一個數據庫中,很容易查詢。

然而,微服務架構中的數據訪問變得復雜許多。每個微服務擁有的數據專門用于該微服務,僅通過其 API 訪問。這種數據封裝保證了微服務松散耦合,并且可以獨立更新。但如果多個服務訪問相同數據,架構更新會耗費時間、也需要所有服務的協調更新。

更糟糕的是,不同的微服務通常使用不同類型的數據庫。現代應用存儲和處理各種類型的數據,而關系型數據庫并非總是好選擇。對于一些使用場景,特定的 NoSQL 數據庫能提供更方便的數據模型、更好的性能和可擴展性。譬如,服務使用 Elasticsearch 這樣的文本搜索引擎來存儲和查詢文本;同樣地,存儲社交圖譜數據的服務可能需要使用 Neo4j 這樣的圖譜數據庫。因此,基于微服務的應用通常會混合使用 SQL 和 NoSQL 數據庫,即多語言留存(polyglot persistence approach)。

分區的、多語言留存的架構對于數據存儲有很多好處,包括服務的松耦合、更好的性能和可擴展性。然而,它也確實給分布式數據管理帶來了挑戰。

第一個挑戰就是如何實現業務邏輯,保持多種服務的一致性。為了說明為何這是一個問題,我們以在線 B2B 商店為例。Customer Service(下文使用客戶服務)維護與用戶有關的信息,包括信用信息。Order Service(下文使用訂單服務)管理訂單,驗證新訂單沒有超出用戶的信用額度。在單體應用里,訂單服務可以簡單地使用 ACID 事務來核對提供的信用信息和創建訂單。

相反,在微服務架構中,如下圖所示,訂單表和客戶表為各自對應的服務私有。

訂單服務無法直接訪問客戶表,只能通過客戶服務提供的 API。訂購服務可能使用分布式事務,也被稱為兩步提交(2PC)。然而,2PC 通常不是現代應用的可行選項。CAP 定理需要用戶在可用性和 ACID 風格的一致性中二選一,通??捎眯允歉玫倪x擇。此外,許多現代技術,譬如大多數 NoSQL 數據庫并不支持 2PC。維護整個服務和數據庫中的數據一致性是至關重要的,因此我們需要另一種解決方案。

第二個挑戰就是如何實現檢索多個服務數據的查詢。假設應用需要顯示一位客戶和他的最近的訂單。如果訂單服務為檢索客戶訂單提供了 API,那么可以使用應用端獲取該數據。應用通過客戶服務檢索該客戶,通過訂單服務檢索該顧客的訂單。但是假如訂單服務只支持通過訂單主鍵查詢訂單(可能使用僅支持鍵值檢索的 NoSQL 數據庫),這種情況下,就沒有合適的方法來檢索所需數據。

事件驅動的架構

對于許多應用,解決方案就是事件驅動的架構。在這一架構里,當有顯著事件發生時,譬如更新業務實體,某個微服務會發布事件,其它微服務則訂閱這些事件。當某一微服務接收到事件就可以更新自己的業務實體,實現更多事件被發布。

用戶能夠使用事件來實現跨多個服務的業務邏輯。事務由一系列步驟組成,每一步都有一個微服務更新業務實體,然后發布觸發下一步的事件。下面的系列圖展示了如何使用事件驅動的方法在創建訂單時檢查可用信用。微服務通過消息代理來交換事件。

  1. 訂單服務創建狀態為 NEW 的訂單,并發布“訂單已創建”事件。
  1. 客戶服務獲取“訂單已創建”事件,為此訂單保留信用,發布“信用保留”事件。
  1. 訂單服務獲取“信用保留”事件,把訂單狀態修改為 OPEN。

更為復雜的場景可能涉及更多的步驟,比如在核對客戶信用的同時預留庫存。

基于(a)每個服務自動更新數據庫和發布事件,以及(b)消息代理確保事件傳遞至少一次,用戶能夠跨多個服務完成業務邏輯。注意它們并非 ACID 業務。這種模式提供弱確定性,比如最終一致性。這種事務模型也被稱作 BASE 模型。

用戶也可以使用事件來維護不同微服務擁有的預連接數據的物化視圖。維護此視圖的服務訂閱相關事件,并更新視圖。例如,維護客戶訂單視圖的客戶訂單視圖更新服務會訂閱由客戶服務和訂單服務發布的事件。

當客戶訂單查看更新服務收到客戶或者訂單事件,就會更新客戶訂單查看的數據存儲。用戶能夠使用類似 MongoDB 的文檔數據庫查看用戶訂單,并為每位客戶存儲一個文檔。用戶訂單預覽查詢服務通過客戶訂單預覽數據存儲,處理來自客戶和最近訂單的請求。

事件驅動的架構有優點也有缺點。它使得事務跨多個服務并提供最終一致性,也可以讓應用維護物化視圖。缺點之一在于,它的編程模型要比使用 ACID 事務的更加復雜。為了從應用級別的失效中恢復,還需要完成補償性事務,例如,如果信用檢查不成功則必須取消訂單。此外,由于臨時事務造成的改變顯而易見,因而應用必須處理不一致的數據。此外,如果應用從物化視圖中讀取的數據沒有更新時,也會遇到不一致的問題。此架構的另一缺點就是用戶必須檢測并忽略重復事件。

實現原子化

事件驅動的架構還存在以原子粒度更新數據庫并發布事件的問題。例如,訂單服務必須在訂單表中插入一行,然后發布“訂單已創建”事件。這兩個操作需要原子化實現。如果服務在更新數據庫之后、發布事件之前崩潰,系統變得不一致。確保原子化的標準做法是使用包含數據庫和消息代理的分布式事務。然而,基于以上描述的 CAP 理論,這并非我們所想。

使用本地事務發布事件

實現原子化的方法是使用多步驟進程來發布事件,該進程只包含本地事務。訣竅就是在存儲業務實體狀態的數據庫中,有一個事件表來充當消息隊列。應用啟動一個(本地)數據庫事務,更新業務實體的狀態,在事件表中插入一個事件,并提交該事務。獨立的應用線程或進程查詢事件表,將事件發不到消息代理,然后使用本地事務標注事件并發布。下圖展示了這一設計。

訂單服務在訂單表中插入一行,然后在事件表中插入“訂單已創建”的事件。時間發布線程或進程在事件表中查詢未發布的事件并發布,然后更新事件表,將該事件標記為已發布。

這種方法優缺點兼具。優點之一是保證每個更新都有對應的事件發布,并且無需依賴 2PC。此外,應用發布業務級別的事件,消除了推斷事件的需要。這種方法也有缺點。由于開發者必須牢記發布事件,因此有很大可能出錯。此外這一方法對于某些使用 NoSQL 數據庫的應用是個挑戰,因為 NoSQL 本身交易和查詢能力有限。

通過此方法,應用使用本地事務來更新狀態和發布事件,排除了對 2PC 的需要。接下來,我們了解使用應用更新狀態實現原子化的方法。

挖掘數據庫事務日志

無需 2PC 實現原子化的另一種方式是由線程或者進程通過挖掘數據庫事務或提交日志來發布事件。應用更新數據庫,數據庫的事務日志記錄這些變更。事務日志挖掘線程或進程讀取這些日志,并把事件發布到消息代理。如下圖所示:

這一方法的范例是開源的 LinkedIn Databus 項目。Databus 挖掘 Oracle 事務日志并發布與之對應的事件。LinkedIn 使用 Databus 維持各種來源的數據存儲與記錄系統一致。

另一個范例則是 AWS DynamoDB 采用的流機制,AWS DynamoDB 是一個可管理的 NoSQL 數據庫。每個 DynamoDB 流包括 DynamoDB 表在過去 24 小時之內的時序變化,包括創建、更新和刪除操作。應用能夠讀取這些變更,將其作為事件發布。

事務日志挖掘具有多個優點。首先,它能保證無需使用 2PC 就能針對每個更新發布事件。其次,通過將日志發布于應用的業務邏輯分離,事務日志挖掘能夠簡化應用。事務日志挖掘也有缺點,主要缺點就是事務日志的格式與每個數據庫對應,甚至隨著數據庫版本而變化。此外,很難從底層事務日志更新記錄中逆向工程這些業務事件。

通過讓應用更新數據庫,事務日志挖掘消除了對 2PC 的需要。接下來我們會討論另一種方法——消除更新,只依賴事件。

使用事件源

通過采用一種截然不同的、以事件為中心的方法來留存業務實體,事件源無需 2PC 實現了原子化。不同于存儲實體的當前狀態,應用存儲狀態改變的事件序列。應用通過重播事件來重構實體的當前狀態。每當業務實體的狀態改變,新事件就被附加到事件列表。鑒于保存事件是一個單一的操作,本質上也是原子化的。

要了解事件源如何運行,可以以訂單實體為例。在傳統的方法中,每個訂單映射為訂單表的一行,例如一個 ORDERLINEITEM 表。使用事件源的時候,訂單服務以狀態更改事件的方式存儲訂單,包括已創建、已批準、已發貨、已取消等。每個事件都包含足夠的數據去重建訂單狀態。

事件長期保存在事件數據庫,使用 API 添加和檢索實體的事件。事件存儲類似上文提及的消息代理,通過 API 讓服務訂閱事件,將所有事件傳達到所有感興趣的訂閱者。事件存儲是事件驅動的微服務架構的支柱。

事件源有不少優點。它解決了實施事件驅動的微服務架構時的一個關鍵問題,能夠只要狀態改變就可靠地發布事件。另外,它也解決了微服務架構中的數據一致性問題。由于儲存事件而不是域對象,它也避免了對象關系抗阻不匹配的問題(object?relational impedance mismatch problem)。事件源提供了 100% 可靠的業務實體變化的審計日志,使得獲取任何時間點的實體狀態成為可能。事件源的另一大優勢在于業務邏輯由松耦合的、事件交換的業務實體構成,便于從單體應用向微服務架構遷移。

事件源也有缺點。由于采用了不同或不熟悉的編程風格,會有學習曲線。事件存儲只直接支持通過主鍵查詢業務實體,用戶還需要使用 Command Query Responsibility Segregation (CQRS) 來完成查詢。因此,應用必須處理最終一致的數據。

總結

在微服務架構中,每個微服務都有其私有數據存儲,不同的微服務可能使用不同的 SQL 和 NoSQL 數據庫。這些數據庫架構帶來便利的同時,也給分布式數據管理帶來挑戰。第一個挑戰就是如何實現業務事務,保持多個服務的一致性。第二個挑戰就是如何從多個服務中檢索數據,實現查詢。

對于許多應用,解決方案就是使用事件驅動的架構。事件驅動的架構帶來的挑戰是如何原子化地更新狀態和發布事件。有幾個方法可以做到這一點,包括把數據庫用作消息隊列、事務日志挖掘和事件源。

文章轉載自:http://blog.daocloud.io/microservices-5/

查看英文原文

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容