微服務(wù)之間的數(shù)據(jù)依賴問題,該如何解決?
共 7038字,需瀏覽 15分鐘
·
2024-08-19 10:30
來源:網(wǎng)絡(luò)
?? 歡迎加入小哈的星球,你將獲得: 專屬的項目實戰(zhàn) / 1v1 提問 / Java 學(xué)習(xí)路線 / 學(xué)習(xí)打卡 / 每月贈書 / 社群討論
新項目:《從零手?jǐn)]:仿小紅書(微服務(wù)架構(gòu))》 正在持續(xù)爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 點擊查看項目介紹; 《從零手?jǐn)]:前后端分離博客項目(全棧開發(fā))》 2期已完結(jié),演示鏈接:http://116.62.199.48/; 截止目前,累計輸出 54w+ 字,講解圖 2330+ 張,還在持續(xù)爆肝中.. 后續(xù)還會上新更多項目,目標(biāo)是將 Java 領(lǐng)域典型的項目都整一波,如秒殺系統(tǒng), 在線商城, IM 即時通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),解鎖全部項目,已有1900+小伙伴加入
![]()
-
1、數(shù)據(jù)冗余的方案 -
2、解耦業(yè)務(wù)邏輯的數(shù)據(jù)同步方案 -
3、最終效果
曾經(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è)計的:嚴(yán)格按照的微服務(wù)劃分原則將商品相關(guān)的職責(zé)存放在商品系統(tǒng)中。因此,在查詢訂單與采購單時,如果查詢字段包含商品字段,我們需要按照如下順序進(jìn)行查詢:
-
先根據(jù)商品字段調(diào)用商品的服務(wù),然后返回匹配的商品信息; -
在訂單或采購單中,通過 IN 語句匹配商品 ID,再關(guān)聯(lián)查詢對應(yīng)的單據(jù)。
為了方便理解這個過程,訂單查詢流程圖如下圖所示:
初期方案設(shè)計完后,很快我們就遇到了一系列問題:
-
隨著商品數(shù)量的增多,匹配的商品越來越多,于是訂單服務(wù)中包含 IN 語句的查詢效率越來越慢 -
商品作為一個核心服務(wù),依賴它的服務(wù)越來越多,同時隨著商品數(shù)據(jù)量的增長,商品服務(wù)已不堪重負(fù),響應(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ù)了 。
但是,如果商品進(jìn)行了更新,我們?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ù)、更新運(yùn)營冗余數(shù)據(jù)等一大堆服務(wù)。那么商品到底是下游服務(wù)還是上游服務(wù)?還能不能安心當(dāng)?shù)讓雍诵姆?wù)?
因此,第一個解決辦法直接被我們否決了,即我們采取的第二個解決辦法——通過消息發(fā)布訂閱的方案 ,因為它存在如下 2 點 優(yōu)勢:
-
商品無須調(diào)用其他服務(wù),它只需要關(guān)注自身邏輯即可,頂多多生成一條消息送到 MQ 。 -
如果訂單、采購等服務(wù)的更新冗余數(shù)據(jù)失敗了,我們使用消息重試機(jī)制 就可以了,最終能保證數(shù)據(jù)的一致性。
此時,我們的架構(gòu)方案如下圖所示:
這個方案看起來已經(jīng)挺完美了,而且市面上基本也是這么做的,不過該方案存在如下幾個問題。
1、在這個方案中,僅僅保存冗余數(shù)據(jù)還遠(yuǎn)遠(yuǎn)不夠,我們還需要將商品分類與生產(chǎn)批號的清單進(jì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é)點消費(fèi)了,為了讓特定的服務(wù)器消費(fèi)特定的消息,我們就需要臨時改動雙方的代碼。不過聯(lián)調(diào)完成后,我們經(jīng)常忘了改回原代碼。
為此,我們不希望針對冗余數(shù)據(jù)這種非核心需求出現(xiàn)如此多的問題,最終決定使用一個特別的同步冗余數(shù)據(jù)方案,接下來我們進(jìn)一步說明。
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)表)。
仔細(xì)計算后,我們發(fā)現(xiàn)之前數(shù)據(jù)冗余的方案中每個訂單都需要保存一份商品的冗余數(shù)據(jù),假設(shè)訂單總數(shù)是 N,商品總數(shù)是 M,而 N 一般遠(yuǎn)遠(yuǎ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)查,之后就算作者不維護(hù)了也可以自我維護(hù),相對比較可控。 -
作者更新活躍; -
自帶監(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)宕機(jī)的情況,反而是那些部署多臺節(jié)點負(fù)載均衡的后臺服務(wù)常常會出現(xiàn)宕機(jī)。
最終,我們總算解決了服務(wù)之間數(shù)據(jù)依賴的問題。
?? 歡迎加入小哈的星球,你將獲得: 專屬的項目實戰(zhàn) / 1v1 提問 / Java 學(xué)習(xí)路線 / 學(xué)習(xí)打卡 / 每月贈書 / 社群討論
新項目:《從零手?jǐn)]:仿小紅書(微服務(wù)架構(gòu))》 正在持續(xù)爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 點擊查看項目介紹; 《從零手?jǐn)]:前后端分離博客項目(全棧開發(fā))》 2期已完結(jié),演示鏈接:http://116.62.199.48/; 截止目前,累計輸出 54w+ 字,講解圖 2330+ 張,還在持續(xù)爆肝中.. 后續(xù)還會上新更多項目,目標(biāo)是將 Java 領(lǐng)域典型的項目都整一波,如秒殺系統(tǒng), 在線商城, IM 即時通訊,Spring Cloud Alibaba 等等,戳我加入學(xué)習(xí),解鎖全部項目,已有1900+小伙伴加入
![]()
2. 船新 IDEA 2024.2 正式發(fā)布,新特性真香!
3. 使用 Java + WebSocket 實現(xiàn)簡單實時雙人協(xié)同 pk 答題
最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點“在看”,關(guān)注公眾號并回復(fù) Java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
PS:因公眾號平臺更改了推送規(guī)則,如果不想錯過內(nèi)容,記得讀完點一下“在看”,加個“星標(biāo)”,這樣每次新文章推送才會第一時間出現(xiàn)在你的訂閱列表里。
點“在看”支持小哈呀,謝謝啦
