原文鏈接:Event-Driven Data Management for Microservices
- 微服務(wù)介紹
- 構(gòu)建微服務(wù)之使用API網(wǎng)關(guān)
- 構(gòu)建微服務(wù)之:微服務(wù)架構(gòu)中的進(jìn)程間通信
- 微服務(wù)中的服務(wù)發(fā)現(xiàn)
- 微服務(wù)之事件驅(qū)動(dòng)的數(shù)據(jù)管理(本文)
- 選擇一種微服務(wù)部署策略
- 重構(gòu)單體應(yīng)用到微服務(wù)
這是使用微服務(wù)架構(gòu)構(gòu)建應(yīng)用系列的第五篇文章.第一篇文章介紹了微服務(wù)架構(gòu)模式并討論了使用微服務(wù)的優(yōu)勢(shì)和劣勢(shì) 纬朝;第二篇和第三篇文章討論了微服務(wù)架構(gòu)不同層面的通信問(wèn)題豫缨;第四篇文章密切探討了服務(wù)發(fā)現(xiàn)的相關(guān)問(wèn)題腺律;本文章呢羡棵,我們換個(gè)口味谆刨,看看微服務(wù)架構(gòu)模式中的分布式的數(shù)據(jù)管理問(wèn)題宁赤。
微服務(wù)與分布式數(shù)據(jù)管理問(wèn)題
一個(gè)單體應(yīng)用一般只有一個(gè)關(guān)系型數(shù)據(jù)庫(kù)扰法,使用一個(gè)關(guān)系型數(shù)據(jù)的優(yōu)勢(shì)是應(yīng)用可以實(shí)現(xiàn)ACID,為業(yè)務(wù)操作進(jìn)行保證:
- Atomicity – 操作是原子性的
- Consistency – 數(shù)據(jù)庫(kù)中的狀態(tài)永遠(yuǎn)是一致的
- Isolation – 盡管事務(wù)是并發(fā)的執(zhí)行衅鹿,但他們表現(xiàn)的像順序執(zhí)行撒踪,事務(wù)之間不會(huì)彼此影響
- Durable – 一旦事務(wù)提交就不能再撤銷
因此,應(yīng)用可以很簡(jiǎn)單的開(kāi)始一個(gè)事務(wù)大渤,操作(增刪改)多條數(shù)據(jù)制妄,然后提交事務(wù)。
使用關(guān)系型數(shù)據(jù)庫(kù)的另一個(gè)巨大優(yōu)勢(shì)是可以使用SQL泵三,一種豐富的聲明式的標(biāo)準(zhǔn)查詢語(yǔ)言耕捞。 你可以很簡(jiǎn)單的寫(xiě)一條關(guān)聯(lián)多個(gè)數(shù)據(jù)表的查詢語(yǔ)句,關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)會(huì)解析SQL來(lái)決定最優(yōu)執(zhí)行方案執(zhí)行該查詢烫幕。你不必關(guān)心諸如數(shù)據(jù)庫(kù)如何訪問(wèn)這種低層次的細(xì)節(jié)問(wèn)題俺抽,另外,你所有的應(yīng)用數(shù)據(jù)在一個(gè)數(shù)據(jù)庫(kù)中较曼,查詢起來(lái)很容易磷斧。
很不幸的是,當(dāng)轉(zhuǎn)換到微服務(wù)架構(gòu)的時(shí)候捷犹,數(shù)據(jù)訪問(wèn)變的越來(lái)越復(fù)雜弛饭。這是因?yàn)槊總€(gè)服務(wù)擁有的數(shù)據(jù)是該服務(wù)所私有的,要想訪問(wèn)該服務(wù)的數(shù)據(jù)只能通過(guò)API伏恐。對(duì)數(shù)據(jù)進(jìn)行封裝可以確保微服務(wù)之間是松散耦合的孩哑,各個(gè)服務(wù)可以獨(dú)立于其他服務(wù)進(jìn)行演進(jìn)。如果多個(gè)服務(wù)訪問(wèn)同樣的數(shù)據(jù)翠桦,那么Schema的更新是很耗時(shí)而且需要協(xié)調(diào)所有服務(wù)進(jìn)行更新横蜒。
不同微服務(wù)可能使用不同的數(shù)據(jù)庫(kù)會(huì)讓事情變得更糟。現(xiàn)代應(yīng)用程序存儲(chǔ)和處理各種各樣的數(shù)據(jù)销凑,關(guān)系數(shù)據(jù)庫(kù)并不總是最好的選擇丛晌。在一些用例下,一種特殊的NoSQL數(shù)據(jù)庫(kù)可能有更加方便的數(shù)據(jù)模型并且提供更好的性能與擴(kuò)展性 斗幼,比如澎蛛,對(duì)一個(gè)存儲(chǔ)和查詢文檔的服務(wù)來(lái)講,使用Elasticsearch這樣的文檔查詢引擎可能更加合適蜕窿;同樣的谋逻,存儲(chǔ)社交圖譜的服務(wù)更合適使用Neo4J這樣的圖數(shù)據(jù)庫(kù) 呆馁。總之毁兆,微服務(wù)架構(gòu)的應(yīng)用經(jīng)常使用SQL和NoSQL的混合存儲(chǔ)浙滤,通常叫做多態(tài)型數(shù)據(jù)持久性方案。
一個(gè)分區(qū)的多態(tài)型的數(shù)據(jù)持久性方案有諸多好處:松耦合气堕、更高的性能纺腊、擴(kuò)展性等,然而這也引入了分布式數(shù)據(jù)管理的挑戰(zhàn)茎芭!
第一個(gè)挑戰(zhàn)就是如何跨多個(gè)服務(wù)實(shí)現(xiàn)業(yè)務(wù)事務(wù)揖膜、維護(hù)數(shù)據(jù)的一致性。為什么這是一個(gè)問(wèn)題梅桩?讓我們看一個(gè)在線B2B商店的例子:用戶服務(wù)維護(hù)諸如用戶信用額度這樣的信息壹粟,訂單服務(wù)管理訂單并且確保新訂單沒(méi)有超過(guò)用戶的信用額度限制。在傳統(tǒng)單體應(yīng)用中摘投,訂單服務(wù)可以在創(chuàng)建新訂單時(shí)簡(jiǎn)單的使用ACID事務(wù)去查看用戶可用的信用額煮寡。
然而在微服務(wù)架構(gòu)中,ORDER 和CUSTOMER表是各個(gè)服務(wù)所私有的犀呼,如下圖所示:
訂單服務(wù)不能直接去訪問(wèn)用戶表而只能使用用戶服務(wù)提供的API幸撕。訂單服務(wù)可能潛在的使用分布式事務(wù),也就是兩段提交外臂,然而現(xiàn)在的應(yīng)用中兩段提交通常不是一個(gè)可行的選擇坐儿。CAP理論要求你在可用性和ACID風(fēng)格的一致性之間進(jìn)行選擇 ,而且宋光。許多流行的技術(shù)貌矿,比如NoSQL并不支持兩段提交,這就尷尬了罪佳!維護(hù)跨服務(wù)和跨數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性是很有必要的逛漫,所以我們需要另一種解決方案。
另一項(xiàng)挑戰(zhàn)就是如何在多服務(wù)中獲取數(shù)據(jù)赘艳。比如酌毡。假設(shè)我們應(yīng)用需要展示用戶信息和他最近的訂單,如果訂單服務(wù)提供API查詢用戶訂單蕾管,那么你可以使用 應(yīng)用程序連接查詢數(shù)據(jù) :應(yīng)用從用戶服務(wù)查詢用戶信息枷踏,從訂單服務(wù)查詢訂單。 假設(shè)掰曾,訂單服務(wù)僅支持主鍵查詢訂單(也許使用了僅支持主鍵查詢的NoSQL)旭蠕,這種情況下,沒(méi)有好的方法來(lái)檢索數(shù)據(jù)。
事件驅(qū)動(dòng)架構(gòu)
對(duì)很多應(yīng)用來(lái)講掏熬,方案就是使用事件驅(qū)動(dòng)架構(gòu)佑稠。在該架構(gòu)中,一個(gè)微服務(wù)在一些業(yè)務(wù)發(fā)生時(shí)發(fā)布一個(gè)事件孽江,比如更新一條業(yè)務(wù)記錄讶坯,其他微服務(wù)會(huì)訂閱該事件番电,當(dāng)微服務(wù)收到事件后會(huì)更新自己的業(yè)務(wù)記錄岗屏,導(dǎo)致其他的事件被發(fā)布。
你可以使用事件實(shí)現(xiàn)跨服務(wù)的業(yè)務(wù)事務(wù)漱办。一個(gè)事務(wù)包含一系列步驟这刷,每個(gè)步驟由某微服務(wù)更新業(yè)務(wù)記錄并發(fā)布事件觸發(fā)下一步驟組成。下面的圖顯示如何使用事件驅(qū)動(dòng)方式在創(chuàng)建訂單時(shí)檢查可用信用額度 娩井,微服務(wù)間通過(guò)Message Broker交換事件暇屋。
- 訂單服務(wù)創(chuàng)建狀態(tài)為NEW的訂單并發(fā)布Order Created事件
Paste_Image.png - 客戶服務(wù)消費(fèi)Order Created事件,為該訂單預(yù)訂信用并發(fā)布Credit Reserved事件
Paste_Image.png - 訂單服務(wù)消費(fèi)Credit Reserved事件并更改訂單狀態(tài)為OPEN
Paste_Image.png
更復(fù)雜的場(chǎng)景可能調(diào)用多個(gè)步驟洞辣,比如保留訂單同時(shí)檢查用戶信用咐刨。
假設(shè)(a)每個(gè)服務(wù)原子性的更新數(shù)據(jù)庫(kù)并發(fā)布事件– 并且– (b) Message Broker保證事件至少被交付一次,那么你就可以實(shí)現(xiàn)跨服務(wù)的業(yè)務(wù)事務(wù)了扬霜。我們必須注意到一點(diǎn)是定鸟,這并不是ACID事務(wù),他們提供更弱一點(diǎn)的最終一致性著瓶,這種事務(wù)模型可以參考 BASE model.
你也可以使用事件來(lái)維護(hù)提前關(guān)聯(lián)了多個(gè)微服務(wù)的物化視圖联予。該服務(wù)通過(guò)訂閱相關(guān)事件并更新視圖,比如材原,用戶訂單視圖通過(guò)訂閱訂單事件和用戶事件來(lái)更新用戶訂單視圖沸久。
當(dāng)客戶訂單視圖更新服務(wù)接收到來(lái)自客戶或者訂單的事件時(shí),它將更新客戶訂單視圖余蟹。你可以使用Mongodb這樣的文檔數(shù)據(jù)庫(kù)為每個(gè)用戶存儲(chǔ)一份文檔來(lái)實(shí)現(xiàn)客戶訂單更新視圖卷胯,客戶訂單視圖查詢服務(wù)可以查詢客戶訂單視圖數(shù)據(jù)庫(kù)提供客戶信息與最近客戶訂單數(shù)據(jù)。
事件驅(qū)動(dòng)架構(gòu)有很多優(yōu)勢(shì)也有一些劣勢(shì)威酒。它使得跨服務(wù)事務(wù)成為可能窑睁,并提供了最終一致性,另一個(gè)優(yōu)勢(shì)就是它使得應(yīng)用可以維護(hù)物化視圖兼搏;但是劣勢(shì)之一是編程模型比ACID事務(wù)模型更為復(fù)雜 卵慰,通常我們要實(shí)現(xiàn)補(bǔ)償事務(wù)來(lái)恢復(fù)應(yīng)用級(jí)別的錯(cuò)誤。比如佛呻,一旦信用額度確認(rèn)失敗裳朋,你必須要取消訂單,并且應(yīng)用要處理不一致的數(shù)據(jù), 這是因?yàn)殡S意的事務(wù)是可見(jiàn)的鲤嫡,應(yīng)用如果讀取未更新的物化視圖送挑,將會(huì)獲取到不一致的數(shù)據(jù),另外一個(gè)劣勢(shì)是暖眼,訂閱必須可以檢測(cè)并忽略重復(fù)的事件惕耕。
實(shí)現(xiàn)原子性
在事件驅(qū)動(dòng)的架構(gòu)中,還有一個(gè)更新數(shù)據(jù)與發(fā)布事件的原子性問(wèn)題诫肠。比如司澎,訂單服務(wù)需要插入一條數(shù)據(jù)到 ORDER表并且發(fā)布Order Created事件,這兩個(gè)操作原子性完成是很有必要的:假如在數(shù)據(jù)庫(kù)更新完成后栋豫,事件發(fā)布之前服務(wù)掛掉了挤安,系統(tǒng)將會(huì)存在不一致。標(biāo)準(zhǔn)確保原子性的方式是使用分布式事務(wù)調(diào)用數(shù)據(jù)庫(kù)系統(tǒng)和Message Broker丧鸯。然而蛤铜,根據(jù)前面我們描述的理由,比如CAP理論丛肢,我們是想避免這么干的围肥。
使用本地事務(wù)發(fā)布事件
應(yīng)用發(fā)布事件并保證原子性的方法之一是采用多步驟本地事務(wù)方法。技巧是有一張EVENT表蜂怎,表其實(shí)就是模擬一個(gè)message queue穆刻。當(dāng)然數(shù)據(jù)庫(kù)中還存著業(yè)務(wù)數(shù)據(jù)的狀態(tài),應(yīng)用開(kāi)始一個(gè)本地?cái)?shù)據(jù)庫(kù)事務(wù)派敷,更新業(yè)務(wù)數(shù)據(jù)記錄并往EVENT表中插入一條數(shù)據(jù)蛹批,最后提交事務(wù)。一個(gè)單獨(dú)的應(yīng)用線程或進(jìn)程輪詢EVENT表篮愉,并根據(jù)查詢結(jié)果往Message Broker推送事件消息腐芍,然后使用本地事務(wù)標(biāo)記事件被發(fā)布。下圖描述了該設(shè)計(jì):
訂單服務(wù)插入數(shù)據(jù)到ORDER表并且插入Order Created event 到EVENT表试躏。數(shù)據(jù)發(fā)布者線程或進(jìn)程輪詢EVENT表中未發(fā)布的事件猪勇,發(fā)布事件,然后更新EVENT表標(biāo)記事件已被發(fā)布颠蕴。
這種方案有優(yōu)勢(shì)也有劣勢(shì)泣刹。優(yōu)勢(shì)之一是保證了在不使用兩段提交前提下事件在每次更新后一定被發(fā)布 ,當(dāng)然犀被,應(yīng)用發(fā)布了業(yè)務(wù)級(jí)別的事件椅您,減少了再去推斷事件類型的麻煩。劣勢(shì)之一是這種方案很容易出錯(cuò)寡键,因?yàn)橐蟪绦騿T必須記得更新后去發(fā)布事件掀泳,該方案另一個(gè)局限是使用NoSQL時(shí),由于NoSQL的事務(wù)和查詢能力局限,實(shí)現(xiàn)起來(lái)困難员舵。
這種方案使用本地事務(wù)來(lái)更新數(shù)據(jù)和發(fā)布事件減少了兩段提交的使用脑沿,現(xiàn)在讓我們來(lái)看只需更新?tīng)顟B(tài)就達(dá)到原子性的方法。
挖掘數(shù)據(jù)庫(kù)事務(wù)日志
另一種不需要兩段提交就實(shí)現(xiàn)原子性的方法是挖掘數(shù)據(jù)庫(kù)事務(wù)日志或提交日志實(shí)現(xiàn)事件發(fā)布,這就要求變化操作要被記錄在數(shù)據(jù)庫(kù)事務(wù)日志當(dāng)中马僻,事務(wù)日志挖據(jù)線程或進(jìn)程讀取事務(wù)日志并發(fā)布事件到 Message Broker庄拇。下圖展示了這種方案:
例子之一就是開(kāi)源的LinkedIn Databus 項(xiàng)目。 Databus 挖掘 Oracle事務(wù)日志并發(fā)布相應(yīng)的事件韭邓, LinkedIn使用 Databus 來(lái)保持各種派生數(shù)據(jù)存儲(chǔ)與記錄系統(tǒng)的一致措近。
另一個(gè)例子就是NoSQL數(shù)據(jù)庫(kù)streams mechanism in AWS DynamoDB, DynamoDB 流包含在過(guò)去 24 小時(shí)向 DynamoDB 表中的記錄所做的時(shí)序性變化 (創(chuàng)建仍秤、 更新和刪除操作)熄诡。應(yīng)用程序可以從流中讀取這些更改并將它們作為事件發(fā)布。
事務(wù)日志挖掘有優(yōu)勢(shì)也有劣勢(shì)诗力。優(yōu)勢(shì)之一是在不使用兩段提交的前提下保證了事件一定被發(fā)布,事務(wù)日志挖據(jù)也可以通過(guò)拆分應(yīng)用業(yè)務(wù)邏輯事件的發(fā)布簡(jiǎn)化整個(gè)應(yīng)用我抠。該方案一個(gè)巨大的劣勢(shì)是:事務(wù)日志每個(gè)數(shù)據(jù)庫(kù)都不同苇本,甚至相同數(shù)據(jù)庫(kù)不同版本也會(huì)不同(攤手),而且我們很難從低級(jí)別事務(wù)日志的更新記錄中反推高級(jí)別的業(yè)務(wù)事件菜拓。
事務(wù)日志挖掘通過(guò)更新數(shù)據(jù)庫(kù)來(lái)避免使用兩段提交“暾現(xiàn)在讓我們看看一種消除了更新,并完全依靠的事件的方案:
使用事件源
事件源通過(guò)使用截然不同纳鼎,以事件為中心的方案持久化業(yè)務(wù)實(shí)體俺夕,達(dá)到了不使用兩段提交前提下 的原子性。這種方案存儲(chǔ)一系列狀態(tài)變化的事件而不是存儲(chǔ)當(dāng)前實(shí)體的狀態(tài)友多。應(yīng)用可以通過(guò)重放事件來(lái)重新構(gòu)建實(shí)體的當(dāng)前狀態(tài)俊性。一旦業(yè)務(wù)實(shí)體發(fā)生變化寒砖,一個(gè)新的事件就會(huì)被添加到事件列表中,由于保存事件是單一操作映九,本身就是原子性的。
為查看事件源如何工作瞎颗,我們以訂單實(shí)體為例子件甥。傳統(tǒng)方案下,每個(gè)訂單會(huì)映射到ORDER表并記錄數(shù)據(jù)到ORDER_LINE_ITEM哼拔。但當(dāng)使用事件源編程時(shí)引有,訂單服務(wù)以存儲(chǔ)訂單狀態(tài)變化事件來(lái)存儲(chǔ)訂單:訂單創(chuàng)建、批準(zhǔn)倦逐、運(yùn)輸譬正、取消。每個(gè)事件有充足的信息來(lái)重新構(gòu)建訂單。
事件存儲(chǔ)到專門存儲(chǔ)事件的數(shù)據(jù)庫(kù)中导帝,數(shù)據(jù)庫(kù)提供添加和查詢實(shí)體事件的API守谓。這個(gè)事件數(shù)據(jù)庫(kù)也會(huì)像我們上面描述的Message Broker一樣提供讓其他服務(wù)訂閱事件的API,事件數(shù)據(jù)庫(kù)向?qū)ζ涓信d趣的訂閱者發(fā)布事件您单,這個(gè)事件數(shù)據(jù)庫(kù)是事件驅(qū)動(dòng)型微服務(wù)的骨架斋荞。
事件源的優(yōu)勢(shì):它解決了一個(gè)實(shí)現(xiàn)事件源架構(gòu)的關(guān)鍵問(wèn)題,使得事件發(fā)生時(shí)可靠的發(fā)布事件成為可能虐秦,因此平酿,它解決了微服務(wù)架構(gòu)中數(shù)據(jù)一致性的問(wèn)題。當(dāng)然悦陋,因?yàn)樗志没录穷I(lǐng)域?qū)ο篁诒耍脖苊饬嗣嫦驅(qū)ο蟮疥P(guān)系型數(shù)據(jù)庫(kù)中的阻抗不匹配問(wèn)題。事件源也為實(shí)體變動(dòng)提供了100%可靠的審計(jì)日志俺驶,并使其能夠執(zhí)行可以確定在任何時(shí)間點(diǎn)實(shí)體狀態(tài)的時(shí)態(tài)查詢幸逆。事件源的另一巨大優(yōu)勢(shì)是,業(yè)務(wù)邏輯與交換事件的實(shí)體是松耦合的暮现,這使得遷移一個(gè)單體應(yīng)用到微服務(wù)架構(gòu)更加簡(jiǎn)單还绘。
事件源也有一些劣勢(shì):對(duì)程序員來(lái)講這是完全不同的,非常陌生的編程風(fēng)格栖袋,學(xué)習(xí)曲線陡峭拍顷。事件數(shù)據(jù)庫(kù)僅僅直接支持主鍵方式查詢業(yè)務(wù)實(shí)體,必須使用 Command Query Responsibility Segregation (CQRS) 來(lái)實(shí)現(xiàn)查詢塘幅。因此昔案,應(yīng)用必須來(lái)處理最終一致性。
總結(jié)
在微服務(wù)架構(gòu)中电媳,每個(gè)服務(wù)擁有自己的數(shù)據(jù)存儲(chǔ)踏揣。不同服務(wù)可能是一不同的SQL或者NoSQL數(shù)據(jù)庫(kù)。這樣的數(shù)據(jù)庫(kù)架構(gòu)有很多優(yōu)勢(shì)匆背,當(dāng)然也帶來(lái)了分布式數(shù)據(jù)管理的挑戰(zhàn)呼伸。挑戰(zhàn)之一就是如何實(shí)現(xiàn)跨多服務(wù)的業(yè)務(wù)邏輯事務(wù)來(lái)維持一致性,挑戰(zhàn)之二就是如何從多服務(wù)查詢數(shù)據(jù)钝尸。
對(duì)很多應(yīng)用來(lái)講括享,使用事件驅(qū)動(dòng)架構(gòu)來(lái)應(yīng)對(duì)這些挑戰(zhàn)。實(shí)現(xiàn)事件驅(qū)動(dòng)架構(gòu)的挑戰(zhàn)之一是如何保證更新數(shù)據(jù)狀態(tài)和發(fā)布事件的原子性 珍促,有幾種方案來(lái)實(shí)現(xiàn)铃辖,包括以數(shù)據(jù)庫(kù)為message queue,事務(wù)日志挖掘猪叙,事件源娇斩。
剩余的文章將討論其他方面的問(wèn)題仁卷。