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

帶你聊技術發表於2023-05-12

來源:JAVA日知錄

大家好,我是飄渺。

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

商品:

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

訂單和子訂單

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

採購單和子訂單

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

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

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

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

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

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

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

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

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

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

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

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

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

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

資料冗餘的方案

資料冗餘的方案說白了就是在訂單、採購單中儲存一些商品欄位資訊。

為了便於你理解,下面我們藉著上方的實際業務場景具體說明下,請注意觀察兩者之間的區別。

商品

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

訂單和子訂單

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

採購單和子訂單

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

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

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

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

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

方案看起來可以滿足我們的需求,但是又產生了一個新的資料一致性問題。

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

資料一致性問題:

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

依賴問題:

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

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

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

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

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

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

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

  1. 在這個方案中,僅僅儲存冗餘資料還遠遠不夠,我們還需要將商品分類與生產批號的清單進行關聯查詢。也就是說,每個服務不只是訂閱商品變更這一種訊息,還需要訂閱商品分類、商品生產批號變更等訊息。下面請注意檢視訂單表結構的紅色加粗部分內容。
如何解決微服務之間的資料依賴問題?
  1. 每個依賴的服務需要重複實現冗餘資料更新同步的邏輯。前面我們講了採購、訂單及其他服務都需要依賴商品資料,因此每個服務需要將冗餘資料的訂閱、更新邏輯做一遍,最終重複的程式碼就會很多。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 它的介面管理不錯;

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

  • 作者更新活躍;

  • 自帶監控報警功能。

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

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

上線效果

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

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

最終,我們總算解決了服務之間資料依賴的問題。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024922/viewspace-2951733/,如需轉載,請註明出處,否則將追究法律責任。

相關文章