微服務之間的資料依賴問題,該如何解決?

陶然陶然發表於2023-11-28

   一、業務場景

  曾經設計的一個供應鏈系統中,存在商品、銷售訂單、採購這三個服務,它們的主資料的部分結構如下所示。  

  在設計這個供應鏈系統時,我們需要滿足以下兩個需求:

  根據商品的型號/分類/生成年份/編碼等查詢訂單;

  根據商品的型號/分類/生成年份/編碼等查詢採購訂單。

  初期的方案是這樣設計的:嚴格按照的微服務劃分原則將商品相關的職責存放在商品系統中。因此,在查詢訂單與採購單時,如果查詢欄位包含商品欄位,需要按照如下順序進行查詢。

  先根據商品欄位呼叫商品的服務,然後返回匹配的商品資訊;

  在訂單或採購單中,透過 IN 語句匹配商品 ID,再關聯查詢對應的單據。

  為了方便理解這個過程,畫了一張訂單查詢流程圖,如下圖所示。  

  初期方案設計完後,很快就遇到了一系列問題。

  隨著商品數量的增多,匹配的商品越來越多,於是訂單服務中包含 IN 語句的查詢效率越來越慢;

  商品作為一個核心服務,依賴它的服務越來越多,同時隨著商品資料量的增長,商品服務已不堪重負,響應速度也變慢,還存在請求超時的情況;

  由於商品服務超時,相關服務處理請求經常失敗。

  結果就是業務方每次查詢訂單或採購單時,只要帶上了商品這個關鍵字,查詢效率就會很慢而且老是失敗。於是,重新想了一個新方案——資料冗餘。

   二、資料冗餘的方案

  資料冗餘的方案說白了就是在訂單、採購單中儲存一些商品欄位資訊。為了便於理解,下面藉著上方的實際業務場景具體說明下,請注意觀察兩者之間的區別。  

  調整架構方案後,每次查詢時,就可以不再依賴商品服務了。

  但是,如果商品進行了更新,我們如何同步冗餘的資料呢?在此分享2種解決辦法。

  每次更新商品時,先呼叫訂單與採購服務,再更新商品的冗餘資料;

  每次更新商品時,先發布一條訊息,訂單與採購服務各自訂閱這條訊息後,再各自更新商品的冗餘資料。

  如果商品服務每次更新商品都需要呼叫訂單與採購服務,然後再更新冗餘資料,則會出現以下兩種問題。

  資料一致性問題:如果訂單與採購的冗餘資料更新失敗了,整個操作都需要回滾。這時商品服務的開發人員肯定不樂意,因為冗餘資料不是商品服務的核心需求,不能因為邊緣流程阻斷了自身的核心流程。

  依賴問題:從職責來說,商品服務應該只關注商品本身,但是現在商品還需要呼叫訂單與採購服務。而且,依賴商品這個核心服務的服務實在是太多了,也就導致後續商品服務每次更新商品時,都需要呼叫更新訂單冗餘資料、更新採購冗餘資料、更新門店庫存冗餘資料、更新運營冗餘資料等一大堆服務。那麼商品到底是下游服務還是上游服務?還能不能安心當底層核心服務?

  因此,第一個解決辦法直接被否決了,即採取的第二個解決辦法——透過訊息釋出訂閱的方案,因為它存在如下2點優勢:

  商品無須呼叫其他服務,它只需要關注自身邏輯即可,頂多多生成一條訊息送到MQ;

  如果訂單、採購等服務的更新冗餘資料失敗了,使用訊息重試機制就可以了,最終能保證資料的一致性。

  此時,架構方案如下圖所示:  

  這個方案看起來已經挺完美了,而且市面上基本也是這麼做的,不過該方案存在如下幾個問題。

  問題1:在這個方案中,僅僅儲存冗餘資料還遠遠不夠,還需要將商品分類與生產批號的清單進行關聯查詢。也就是說,每個服務不只是訂閱商品變更這一種訊息,還需要訂閱商品分類、商品生產批號變更等訊息。下面請注意檢視訂單表結構的紅色加粗部分內容。  

  只是列舉了一部分的結構,事實上,商品表中還有很多欄位存在冗餘,比如保修型別、包換型別等。為了更新這些冗餘資料,採購服務與訂單服務往往需要訂閱近十種訊息,因此,基本上需要把商品的一小半邏輯複製過來

  問題2:每個依賴的服務需要重複實現冗餘資料更新同步的邏輯。前面講了採購、訂單及其他服務都需要依賴商品資料,因此每個服務需要將冗餘資料的訂閱、更新邏輯做一遍,最終重複的程式碼就會很多

  問題3:MQ 訊息型別太多了。聯調時最麻煩的是 MQ 之間的聯動,如果是介面聯調還好說,因為呼叫哪個伺服器的介面相對可控而且比較好追溯;如果是訊息聯調就比較麻煩,因為常常不知道某條訊息被哪臺服務節點消費了,為了讓特定的伺服器消費特定的訊息,就需要臨時改動雙方的程式碼。不過聯調完成後,經常忘了改回原始碼。

  為此,我們不希望針對冗餘資料這種非核心需求出現如此多的問題,最終決定使用一個特別的同步冗餘資料方案。

   三、解耦業務邏輯的資料同步方案

  解耦業務邏輯的資料同步方案的設計思路是這樣的:

  將商品及商品相關的一些表(比如分類表、生產批號表、保修型別、包換型別等)實時同步到需要依賴使用它們的服務的資料庫,並且保持表結構不變;

  在查詢採購、訂單等服務時,直接關聯同步過來的商品相關表;

  不允許採購、訂單等服務修改商品相關表。

  此時,整個方案的架構如下圖所示。  

  以上方案就能輕鬆解決如下兩個問題:

  商品無須依賴其他服務,如果其他服務的冗餘資料同步失敗,它也不需要回滾自身的流程;

  採購、訂單等服務無須關注冗餘資料的同步。

  不過,該方案的“缺點”是增加了訂單、採購等資料庫的儲存空間(因為增加了商品相關表)

  仔細計算後,發現之前資料冗餘的方案中每個訂單都需要儲存一份商品的冗餘資料,假設訂單總數是 N,商品總數是 M,而 N 一般遠遠大於 M。因此,在之前資料冗餘的方案中,N 條訂單就會產生 N 條商品的冗餘資料。相比之下,解耦業務邏輯的資料同步方案更省空間,因為只增加了 M 條商品的資料。

  此時問題又來了,如何實時同步相關表的資料呢?直接找一個現成的開源中介軟體就可以了,不過它需要滿足支援實時同步、支援增量同步、不用寫業務邏輯、支援 MySQL 之間同步、活躍度高這五點要求。

  根據這五點要求,在市面上找了一圈,發現了 Canal、Debezium、DataX、Databus、Flinkx、Bifrost 這幾款開源中介軟體,它們之間的區別如下表所示。  

  從對比表中來看,比較貼近我們需求的開源中介軟體是 Bifrost,原因如下:

  它的介面管理不錯;

  它的架構比較簡單,出現問題後,可以自行調查,之後就算作者不維護了也可以自我維護,相對比較可控;

  作者更新活躍;

  自帶監控報警功能。

  因此,最終使用了 Bifrost 開源中介軟體,此時整個方案的架構如下圖所示。  

   四、上線效果

  整個架構方案上線後,商品資料的同步還算比較穩定,此時商品服務的開發人員只需要關注自身邏輯,無須再關注使用資料的人。如果需要關聯使用商品資料的訂單,採購服務的開發人員也無須關注商品資料的同步問題,只需要在查詢時加上關聯語句即可,實現了雙贏。

  然而,唯一讓我們擔心的是 Bifrost 不支援叢集,沒法保障高可用性。不過,到目前為止,它還沒有出現當機的情況,反而是那些部署多臺節點負載均衡的後臺服務常常會出現當機。

來自 “ dbaplus社群 ”, 原文作者:知識記錄者;原文連結:https://server.it168.com/a2023/1122/6830/000006830608.shtml,如有侵權,請聯絡管理員刪除。

相關文章