轉自:http://dockone.io/article/936nginx
【編者的話】本文是使用微服務建立應用系列的第五篇文章。第一篇文章介紹了微服務架構模式,而且討論了使用微服務的優缺點;第二和第三篇描述了微服務架構模塊間通信的不一樣方面;第四篇研究了服務發現中的問題。本篇中,咱們從另一個角度研究一下微服務架構帶來的分佈式數據管理問題。數據庫
單體式應用通常都會有一個關係型數據庫,由此帶來的好處是應用可使用 ACID transactions,能夠帶來一些重要的操做特性:編程
鑑於以上特性,應用能夠簡化爲:開始一個交易,改變(插入,刪除,更新)不少行,而後提交這些交易。
使用關係型數據庫帶來另一個優點在於提供SQL(功能強大,可聲明的,錶轉化的查詢語言)支持。用戶能夠很是容易經過查詢將多個表的數據組合起來,RDBMS查詢調度器決定最佳實現方式,用戶不須要擔憂例如如何訪問數據庫等底層問題。另外,由於全部應用的數據都在一個數據庫中,很容易去查詢。
然而,對於微服務架構來講,數據訪問變得很是複雜,這是由於數據都是微服務私有的,惟一可訪問的方式就是經過API。這種打包數據訪問方式使得微服務之間鬆耦合,而且彼此之間獨立。若是多個服務訪問同一個數據,schema會更新訪問時間,並在全部服務之間進行協調。
更甚於,不一樣的微服務常用不一樣的數據庫。應用會產生各類不一樣數據,關係型數據庫並不必定是最佳選擇。某些場景,某個NoSQL數據庫可能提供更方便的數據模型,提供更加的性能和可擴展性。例如,某個產生和查詢字符串的應用採用例如Elasticsearch的字符搜索引擎。一樣的,某個產生社交圖片數據的應用能夠採用圖片數據庫,例如,Neo4j;所以,基於微服務的應用通常都使用SQL和NoSQL結合的數據庫,也就是被稱爲polyglot persistence的方法。
分區的,polyglot-persistent架構用於存儲數據有許多優點,包括鬆耦合服務和更佳性能和可擴展性。然而,隨之而來的則是分佈式數據管理帶來的挑戰。
第一個挑戰在於如何完成一筆交易的同時保持多個服務之間數據一致性。之因此會有這個問題,咱們以一個在線B2B商店爲例,客戶服務維護包括客戶的各類信息,例如credit lines。訂單服務管理訂單,須要驗證某個新訂單與客戶的信用限制沒有衝突。在單一式應用中,訂單服務只須要使用ACID交易就能夠檢查可用信用和建立訂單。
相反的,微服務架構下,訂單和客戶表分別是相對應服務的私有表,以下圖所示:架構
訂單服務不能直接訪問客戶表,只能經過客戶服務發佈的API來訪問。訂單服務也可使用 distributed transactions, 也就是周知的兩階段提交 (2PC)。然而,2PC在如今應用中不是可選性。根據CAP理論,必須在可用性(availability)和ACID一致性(consistency)之間作出選擇,availability通常是更好的選擇。可是,許多現代科技,例如許多NoSQL數據庫,並不支持2PC。在服務和數據庫之間維護數據一致性是很是根本的需求,所以咱們須要找其餘的方案。
第二個挑戰是如何完成從多個服務中搜索數據。例如,設想應用須要顯示客戶和他的訂單。若是訂單服務提供API來接受用戶訂單信息,那麼用戶可使用類應用型的join操做接收數據。應用從用戶服務接受用戶信息,從訂單服務接受此用戶訂單。假設,訂單服務只支持經過私有鍵(key)來查詢訂單(也許是在使用只支持基於主鍵接受的NoSQL數據庫),此時,沒有合適的方法來接收所需數據。併發
對許多應用來講,這個解決方案就是使用事件驅動架構(event-driven architecture)。在這種架構中,當某件重要事情發生時,微服務會發佈一個事件,例如更新一個業務實體。當訂閱這些事件的微服務接收此事件時,就能夠更新本身的業務實體,也可能會引起更多的時間發佈。
可使用事件來實現跨多服務的業務交易。交易通常由一系列步驟構成,每一步驟都由一個更新業務實體的微服務和發佈激活下一步驟的事件構成。下圖展示如何使用事件驅動方法,在建立訂單時檢查信用可用度,微服務經過消息代理(Messsage Broker)來交換事件。分佈式
考慮到(a)每一個服務原子性更新數據庫和發佈事件,而後,(b)消息代理確保事件傳遞至少一次,而後能夠跨多個服務完成業務交易(此交易不是ACID交易)。這種模式提供弱肯定性,例如最終一致性 eventual consistency。這種交易類型被稱做 BASE model。
亦可使用事件來維護不一樣微服務擁有數據預鏈接(pre-join)的實現視圖。維護此視圖的服務訂閱相關事件而且更新視圖。例如,客戶訂單視圖更新服務(維護客戶訂單視圖)會訂閱由客戶服務和訂單服務發佈的事件。微服務
當客戶訂單視圖更新服務收到客戶或者訂單事件,就會更新 客戶訂單視圖數據集。可使用文檔數據庫(例如MongoDB)來實現客戶訂單視圖,爲每一個用戶存儲一個文檔。客戶訂單視圖查詢服務負責響應對客戶以及最近訂單(經過查詢客戶訂單視圖數據集)的查詢。
事件驅動架構也是既有優勢也有缺點,此架構可使得交易跨多個服務且提供最終一致性,而且可使應用維護最終視圖;而缺點在於編程模式比ACID交易模式更加複雜:爲了從應用層級失效中恢復,還須要完成補償性交易,例如,若是信用檢查不成功則必須取消訂單;另外,應用必須應對不一致的數據,這是由於臨時(in-flight)交易形成的改變是可見的,另外當應用讀取未更新的最終視圖時也會碰見數據不一致問題。另一個缺點在於訂閱者必須檢測和忽略冗餘事件。性能
事件驅動架構還會碰到數據庫更新和發佈事件原子性問題。例如,訂單服務必須向ORDER表插入一行,而後發佈Order Created event,這兩個操做須要原子性。若是更新數據庫後,服務癱了(crashes)形成事件未能發佈,系統變成不一致狀態。確保原子操做的標準方式是使用一個分佈式交易,其中包括數據庫和消息代理。然而,基於以上描述的CAP理論,這卻並非咱們想要的。學習
得到原子性的一個方法是對發佈事件應用採用multi-step process involving only local transactions,技巧在於一個EVENT表,此表在存儲業務實體數據庫中起到消息列表功能。應用發起一個(本地)數據庫交易,更新業務實體狀態,向EVENT表中插入一個事件,而後提交這次交易。另一個獨立應用進程或者線程查詢此EVENT表,向消息代理髮布事件,而後使用本地交易標誌此事件爲已發佈,以下圖所示:ui
訂單服務向ORDER表插入一行,而後向EVENT表中插入Order Created event,事件發佈線程或者進程查詢EVENT表,請求未發佈事件,發佈他們,而後更新EVENT表標誌此事件爲已發佈。
此方法也是優缺點都有。優勢是能夠確保事件發佈不依賴於2PC,應用發佈業務層級事件而不須要推斷他們發生了什麼;而缺點在於此方法因爲開發人員必須牢記發佈事件,所以有可能出現錯誤。另外此方法對於某些使用NoSQL數據庫的應用是個挑戰,由於NoSQL自己交易和查詢能力有限。
此方法由於應用採用了本地交易更新狀態和發佈事件而不須要2PC,如今再看看另一種應用簡單更新狀態得到原子性的方法。
另一種不須要2PC而得到線程或者進程發佈事件原子性的方式就是挖掘數據庫交易或者提交日誌。應用更新數據庫,在數據庫交易日誌中產生變化,交易日誌挖掘進程或者線程讀這些交易日誌,將日誌發佈給消息代理。以下圖所見:
此方法的例子如LinkedIn Databus 項目,Databus 挖掘Oracle交易日誌,根據變化發佈事件,LinkedIn使用Databus來保證系統內各記錄之間的一致性。
另外的例子如:AWS的 streams mechanism in AWS DynamoDB,是一個可管理的NoSQL數據庫,一個DynamoDB流是由過去24小時對數據庫表基於時序的變化(建立,更新和刪除操做),應用能夠從流中讀取這些變化,而後以事件方式發佈這些變化。
交易日誌挖掘也是優缺點並存。優勢是確保每次更新發布事件不依賴於2PC。交易日誌挖掘能夠經過將發佈事件和應用業務邏輯分離開獲得簡化;而主要缺點在於交易日誌對不一樣數據庫有不一樣格式,甚至不一樣數據庫版本也有不一樣格式;並且很難從底層交易日誌更新記錄轉換爲高層業務事件。
交易日誌挖掘方法經過應用直接更新數據庫而不須要2PC介入。下面咱們再看一種徹底不一樣的方法:不須要更新只依賴事件的方法。
Event sourcing (事件源)經過使用根本不一樣的事件中心方式來得到不需2PC的原子性,保證業務實體的一致性。 這種應用保存業務實體一系列狀態改變事件,而不是存儲實體如今的狀態。應用能夠經過重放事件來重建實體如今狀態。只要業務實體發生變化,新事件就會添加到時間表中。由於保存事件是單一操做,所以確定是原子性的。
爲了理解事件源工做方式,考慮事件實體做爲一個例子。傳統方式中,每一個訂單映射爲ORDER表中一行,例如在ORDER_LINE_ITEM表中。可是對於事件源方式,訂單服務以事件狀態改變方式存儲一個訂單:建立的,已批准的,已發貨的,取消的;每一個事件包括足夠數據來重建訂單狀態。
事件是長期保存在事件數據庫中,提供API添加和獲取實體事件。事件存儲跟以前描述的消息代理相似,提供API來訂閱事件。事件存儲將事件遞送到全部感興趣的訂閱者,事件存儲是事件驅動微服務架構的基幹。
事件源方法有不少優勢:解決了事件驅動架構關鍵問題,使得只要有狀態變化就能夠可靠地發佈事件,也就解決了微服務架構中數據一致性問題。另外,由於是持久化事件而不是對象,也就避免了object relational impedance mismatch problem。
數據源方法提供了100%可靠的業務實體變化監控日誌,使得獲取任什麼時候點實體狀態成爲可能。另外,事件源方法可使得業務邏輯能夠由事件交換的鬆耦合業務實體構成。這些優點使得單體應用移植到微服務架構變的相對容易。
事件源方法也有很多缺點,由於採用不一樣或者不太熟悉的變成模式,使得從新學習不太容易;事件存儲只支持主鍵查詢業務實體,必須使用 Command Query Responsibility Segregation (CQRS) 來完成查詢業務,所以,應用必須處理最終一致數據。
在微服務架構中,每一個微服務都有本身私有的數據集。不一樣微服務可能使用不一樣的SQL或者NoSQL數據庫。儘管數據庫架構有很強的優點,可是也面對數據分佈式管理的挑戰。第一個挑戰就是如何在多服務之間維護業務交易一致性;第二個挑戰是如何從多服務環境中獲取一致性數據。
最佳解決辦法是採用事件驅動架構。其中碰到的一個挑戰是如何原子性的更新狀態和發佈事件。有幾種方法能夠解決此問題,包括將數據庫視爲消息隊列、交易日誌挖掘和事件源。
在將來的博客中,將會跟深刻探討微服務的其餘方面。
本系列七篇文章中其它幾篇連接以下:
原文連接:Event-Driven Data Management for Microservices(翻譯:楊峯)