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

自動駕駛技術(shù)涉及到的AI科技

微服務(wù)和容器之間的有何關(guān)系?
微服務(wù)循環(huán)依賴調(diào)用引發(fā)的血案
微服務(wù)架構(gòu)中的服務(wù)之間如何互相調(diào)用呢?
從分層架構(gòu)到微服務(wù)架構(gòu)介紹(五)

評論