曾經(jīng)設(shè)計(jì)的一個(gè)供應(yīng)鏈系統(tǒng)中,存在商品 、銷(xiāo)售訂單 、采購(gòu) 這三個(gè)服務(wù),它們的主數(shù)據(jù)的部分結(jié)構(gòu)如下所示:
商品 :
ID | 名稱(chēng) | 分類(lèi) | 型號(hào) | 生產(chǎn)年份 | 編碼 |
---|---|---|---|---|---|
訂單和子訂單 :
訂單ID | 下單時(shí)間 | 客戶(hù) | 總金額 | 子訂單ID | 商品ID | 單價(jià) | 數(shù)量 |
---|---|---|---|---|---|---|---|
采購(gòu)單和子訂單 :
采購(gòu)單ID | 下單時(shí)間 | 供應(yīng)商 | 總金額 | 采購(gòu)子訂單ID | 商品ID | 單價(jià) | 數(shù)量 |
---|---|---|---|---|---|---|---|
在設(shè)計(jì)這個(gè)供應(yīng)鏈系統(tǒng)時(shí),我們需要滿(mǎn)足以下兩個(gè)需求:
根據(jù)商品的型號(hào)/分類(lèi)/生成年份/編碼 等查找訂單;
根據(jù)商品的型號(hào)/分類(lèi)/生成年份/編碼 等查找采購(gòu)訂單。
初期我們的方案是這樣設(shè)計(jì)的:嚴(yán)格按照的微服務(wù)劃分原則將商品相關(guān)的職責(zé)存放在商品系統(tǒng)中。因此,在查詢(xún)訂單與采購(gòu)單時(shí),如果查詢(xún)字段包含商品字段,我們需要按照如下順序進(jìn)行查詢(xún):
先根據(jù)商品字段調(diào)用商品的服務(wù),然后返回匹配的商品信息;
在訂單或采購(gòu)單中,通過(guò) IN 語(yǔ)句匹配商品 ID,再關(guān)聯(lián)查詢(xún)對(duì)應(yīng)的單據(jù)。
為了方便理解這個(gè)過(guò)程,訂單查詢(xún)流程圖如下圖所示:
初期方案設(shè)計(jì)完后,很快我們就遇到了一系列問(wèn)題:
隨著商品數(shù)量的增多,匹配的商品越來(lái)越多,于是訂單服務(wù)中包含 IN 語(yǔ)句的查詢(xún)效率越來(lái)越慢
商品作為一個(gè)核心服務(wù),依賴(lài)它的服務(wù)越來(lái)越多,同時(shí)隨著商品數(shù)據(jù)量的增長(zhǎng),商品服務(wù)已不堪重負(fù),響應(yīng)速度也變慢,還存在請(qǐng)求超時(shí) 的情況
由于商品服務(wù)超時(shí),相關(guān)服務(wù)處理請(qǐng)求經(jīng)常失敗。
結(jié)果就是業(yè)務(wù)方每次查詢(xún)訂單或采購(gòu)單時(shí),只要帶上了商品這個(gè)關(guān)鍵字,查詢(xún)效率就會(huì)很慢而且老是失敗。于是,我們重新想了一個(gè)新方案——數(shù)據(jù)冗余 ,下面我們一起來(lái)看下。
1、數(shù)據(jù)冗余的方案
數(shù)據(jù)冗余說(shuō)白了就是在訂單、采購(gòu)單中保存一些商品字段 信息。
為了方便理解,我們借助上面實(shí)際業(yè)務(wù)場(chǎng)景具體說(shuō)明下,看看兩者的區(qū)別。
商品 :
ID | 名稱(chēng) | 分類(lèi)ID | 型號(hào) | 生產(chǎn)年份ID | 編碼 |
---|---|---|---|---|---|
訂單和子訂單 :
訂單ID | 下單時(shí)間 | 客戶(hù) | 總金額 | ||||
---|---|---|---|---|---|---|---|
子訂單ID | 商品ID | 單價(jià) | 數(shù)量 | 商品名稱(chēng) | 商品分類(lèi)ID | 商品型號(hào) | 生產(chǎn)批次ID |
采購(gòu)單和子訂單 :
采購(gòu)單ID | 下單時(shí)間 | 供應(yīng)商 | 總金額 | ||||
---|---|---|---|---|---|---|---|
采購(gòu)子訂單ID | 商品ID | 單價(jià) | 數(shù)量 | 商品名稱(chēng) | 商品分類(lèi)ID | 商品型號(hào) | 生產(chǎn)批次ID |
調(diào)整架構(gòu)方案后,每次查詢(xún)時(shí),我們就可以不再依賴(lài)商品服務(wù)了 。
但是,如果商品進(jìn)行了更新,我們?nèi)绾瓮饺哂嗟臄?shù)據(jù)呢?在此分享2種 解決辦法。
每次更新商品時(shí),先調(diào)用訂單與采購(gòu)服務(wù),再更新商品的冗余數(shù)據(jù)。
每次更新商品時(shí),先發(fā)布一條消息,訂單與采購(gòu)服務(wù)各自訂閱這條消息后,再各自更新商品冗余數(shù)據(jù)。
那么這2種方案會(huì)出現(xiàn)哪些問(wèn)題呢?
如果商品服務(wù)每次更新商品都要調(diào)用訂單與采購(gòu)服務(wù),然后再更新冗余數(shù)據(jù),則會(huì)出現(xiàn)以下兩種問(wèn)題。
數(shù)據(jù)一致性問(wèn)題 :如果訂單與采購(gòu)的冗余數(shù)據(jù)更新失敗了,整個(gè)操作都需要回滾。這時(shí)商品服務(wù)的開(kāi)發(fā)人員肯定不樂(lè)意,因?yàn)槿哂鄶?shù)據(jù)不是商品服務(wù)的核心需求,不能因?yàn)檫吘壛鞒套钄嗔俗陨淼暮诵牧鞒獭?/p>
依賴(lài)問(wèn)題 :從職責(zé)來(lái)說(shuō),商品服務(wù)應(yīng)該只關(guān)注商品本身,但是現(xiàn)在商品還需要調(diào)用訂單與采購(gòu)服務(wù)。而且,依賴(lài)商品這個(gè)核心服務(wù)的服務(wù)實(shí)在是太多了,也就導(dǎo)致后續(xù)商品服務(wù)每次更新商品時(shí),都需要調(diào)用更新訂單冗余數(shù)據(jù)、更新采購(gòu)冗余數(shù)據(jù)、更新門(mén)店庫(kù)存冗余數(shù)據(jù)、更新運(yùn)營(yíng)冗余數(shù)據(jù)等一大堆服務(wù)。那么商品到底是下游服務(wù)還是上游服務(wù)?還能不能安心當(dāng)?shù)讓雍诵姆?wù)?
因此,第一個(gè)解決辦法直接被我們否決了,即我們采取的第二個(gè)解決辦法——通過(guò)消息發(fā)布訂閱的方案 ,因?yàn)樗嬖谌缦?2 點(diǎn) 優(yōu)勢(shì):
商品無(wú)須調(diào)用其他服務(wù),它只需要關(guān)注自身邏輯即可,頂多多生成一條消息送到 MQ 。
如果訂單、采購(gòu)等服務(wù)的更新冗余數(shù)據(jù)失敗了,我們使用消息重試機(jī)制 就可以了,最終能保證數(shù)據(jù)的一致性。
此時(shí),我們的架構(gòu)方案如下圖所示:
這個(gè)方案看起來(lái)已經(jīng)挺完美了,而且市面上基本也是這么做的,不過(guò)該方案存在如下幾個(gè)問(wèn)題。
1、在這個(gè)方案中,僅僅保存冗余數(shù)據(jù)還遠(yuǎn)遠(yuǎn)不夠,我們還需要將商品分類(lèi)與生產(chǎn)批號(hào)的清單進(jìn)行關(guān)聯(lián)查詢(xún)。也就是說(shuō),每個(gè)服務(wù)不只是訂閱商品變更這一種消息,還需要訂閱商品分類(lèi)、商品生產(chǎn)批號(hào)變更等消息。下面請(qǐng)注意查看訂單表結(jié)構(gòu)的紅色加粗部分內(nèi)容。
訂單ID | 下單時(shí)間 | 客戶(hù) | 總金額 | ||||
---|---|---|---|---|---|---|---|
子訂單ID | 商品ID | 單價(jià) | 數(shù)量 | 商品名稱(chēng) | 商品分類(lèi)ID | 商品型號(hào) | 生產(chǎn)批次ID |
以上只是列舉了一部分的結(jié)構(gòu),事實(shí)上,商品表中還有很多字段存在冗余,比如保修類(lèi)型、包換類(lèi)型等。為了更新這些冗余數(shù)據(jù),采購(gòu)服務(wù)與訂單服務(wù)往往需要訂閱近十種消息,因此,我們基本上需要把商品的一小半邏輯復(fù)制過(guò)來(lái)。
2、每個(gè)依賴(lài)的服務(wù)需要重復(fù)實(shí)現(xiàn)冗余數(shù)據(jù)更新同步的邏輯。前面我們講了采購(gòu)、訂單及其他服務(wù)都需要依賴(lài)商品數(shù)據(jù),因此每個(gè)服務(wù)需要將冗余數(shù)據(jù)的訂閱、更新邏輯做一遍,最終重復(fù)的代碼就會(huì)很多。
3、MQ 消息類(lèi)型太多了:聯(lián)調(diào)時(shí)最麻煩的是 MQ 之間的聯(lián)動(dòng),如果是接口聯(lián)調(diào)還好說(shuō),因?yàn)檎{(diào)用哪個(gè)服務(wù)器的接口相對(duì)可控而且比較好追溯;如果是消息聯(lián)調(diào)就比較麻煩,因?yàn)槲覀兂32恢滥硹l消息被哪臺(tái)服務(wù)節(jié)點(diǎn)消費(fèi)了,為了讓特定的服務(wù)器消費(fèi)特定的消息,我們就需要臨時(shí)改動(dòng)雙方的代碼。不過(guò)聯(lián)調(diào)完成后,我們經(jīng)常忘了改回原代碼。
為此,我們不希望針對(duì)冗余數(shù)據(jù)這種非核心需求出現(xiàn)如此多的問(wèn)題,最終決定使用一個(gè)特別的同步冗余數(shù)據(jù)方案,接下來(lái)我們進(jìn)一步說(shuō)明。
2、解耦業(yè)務(wù)邏輯的數(shù)據(jù)同步方案
解耦業(yè)務(wù)邏輯的數(shù)據(jù)同步方案的設(shè)計(jì)思路是這樣的:
將商品及商品相關(guān)的一些表(比如分類(lèi)表、生產(chǎn)批號(hào)表、保修類(lèi)型、包換類(lèi)型等)實(shí)時(shí)同步到需要依賴(lài)使用它們的服務(wù)的數(shù)據(jù)庫(kù),并且保持表結(jié)構(gòu)不變;
在查詢(xún)采購(gòu)、訂單等服務(wù)時(shí),直接關(guān)聯(lián)同步過(guò)來(lái)的商品相關(guān)表;
不允許采購(gòu)、訂單等服務(wù)修改商品相關(guān)表。
此時(shí),整個(gè)方案的架構(gòu)如下圖所示:
以上方案就能輕松解決如下兩個(gè)問(wèn)題:
商品無(wú)須依賴(lài)其他服務(wù),如果其他服務(wù)的冗余數(shù)據(jù)同步失敗,它也不需要回滾自身的流程;
采購(gòu)、訂單等服務(wù)無(wú)須關(guān)注冗余數(shù)據(jù)的同步。
不過(guò),該方案的“缺點(diǎn) ”是增加了訂單、采購(gòu)等數(shù)據(jù)庫(kù)的存儲(chǔ)空間(因?yàn)樵黾恿松唐废嚓P(guān)表)。
仔細(xì)計(jì)算后,我們發(fā)現(xiàn)之前數(shù)據(jù)冗余的方案中每個(gè)訂單都需要保存一份商品的冗余數(shù)據(jù),假設(shè)訂單總數(shù)是 N,商品總數(shù)是 M,而 N 一般遠(yuǎn)遠(yuǎn)大于 M。因此,在之前數(shù)據(jù)冗余的方案中,N 條訂單就會(huì)產(chǎn)生 N 條商品的冗余數(shù)據(jù)。相比之下,解耦業(yè)務(wù)邏輯的數(shù)據(jù)同步方案更省空間,因?yàn)橹辉黾恿?M 條商品的數(shù)據(jù)。
此時(shí)問(wèn)題又來(lái)了,如何實(shí)時(shí)同步相關(guān)表的數(shù)據(jù)呢?
我們直接找一個(gè)現(xiàn)成的開(kāi)源中間件就可以了,不過(guò)它需要滿(mǎn)足支持實(shí)時(shí)同步、支持增量同步、不用寫(xiě)業(yè)務(wù)邏輯、支持 MySQL 之間同步、活躍度高這五點(diǎn)要求。
根據(jù)這五點(diǎn)要求,我們?cè)谑忻嫔险伊艘蝗?,發(fā)現(xiàn)了 Canal 、Debezium 、DataX 、Databus 、Flinkx 、Bifrost 這幾款開(kāi)源中間件,它們之間的區(qū)別如下表所示:
從對(duì)比表中來(lái)看,比較貼近我們需求的開(kāi)源中間件是 Bifrost ,原因如下:
它的界面管理不錯(cuò);
它的架構(gòu)比較簡(jiǎn)單,出現(xiàn)問(wèn)題后,我們可以自行調(diào)查,之后就算作者不維護(hù)了也可以自我維護(hù),相對(duì)比較可控。
作者更新活躍;
自帶監(jiān)控報(bào)警功能。
因此,最終我們使用了 Bifrost 開(kāi)源中間件,此時(shí)整個(gè)方案的架構(gòu)如下圖所示:
3、最終效果
整個(gè)架構(gòu)方案上線后,商品數(shù)據(jù)的同步還算比較穩(wěn)定,此時(shí)商品服務(wù)的開(kāi)發(fā)人員只需要關(guān)注自身邏輯,無(wú)須再關(guān)注使用數(shù)據(jù)的人。如果需要關(guān)聯(lián)使用商品數(shù)據(jù)的訂單,采購(gòu)服務(wù)的開(kāi)發(fā)人員也無(wú)須關(guān)注商品數(shù)據(jù)的同步問(wèn)題,只需要在查詢(xún)時(shí)加上關(guān)聯(lián)語(yǔ)句即可,實(shí)現(xiàn)了雙贏。
然而,唯一讓我們擔(dān)心的是 Bifrost 不支持集群,沒(méi)法保障高可用性。不過(guò),到目前為止,它還沒(méi)有出現(xiàn)宕機(jī)的情況,反而是那些部署多臺(tái)節(jié)點(diǎn)負(fù)載均衡的后臺(tái)服務(wù)常常會(huì)出現(xiàn)宕機(jī)。
最終,我們總算解決了服務(wù)之間數(shù)據(jù)依賴(lài)的問(wèn)題。
審核編輯:劉清
-
解耦控制
+關(guān)注
關(guān)注
0文章
29瀏覽量
10216 -
MYSQL數(shù)據(jù)庫(kù)
+關(guān)注
關(guān)注
0文章
96瀏覽量
9392 -
Bifrost架構(gòu)
+關(guān)注
關(guān)注
0文章
2瀏覽量
3277
原文標(biāo)題:微服務(wù)之間的數(shù)據(jù)依賴(lài)問(wèn)題,該如何解決?
文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論