訊息抽象層設計和實現-OSS.DataFlow

KevinCC發表於2021-11-23

  前面已經介紹了訊息生產消費中間類庫(OSS.DataFlow)的簡單使用,這篇主要介紹內部的設計實現。主要內容包含:

  1. 訊息生產消費的抽象設計。

  2. 具體使用示例

一. 訊息生產消費的抽象設計。

  需要首先強調的是,這裡的生產消費抽象主要在業務使用層面,拋開具體的RabbitMQ之類的訊息佇列產品。可能說起來比較模糊,我們先看下常見的業務呼叫訊息佇列的情況:

  上邊是一個常見的通過訊息佇列將業務非同步拆解的模型,按道理結構已經十分簡單了,特別是對於一個相對穩定的業務,程式碼基本變動不大的情況下,這個模型基本足夠了。不過對於一個愛折騰的開發人,不玩點花裡胡哨的,存在感就沒了,經過各種假設論證之後,終於找到了這麼幾個不滿意的地方:

 

  1.  開發,測試,生產,都需要搭建對應的Rabbit例項,特別是開發測試環境搶訊息,再加上多人開發,大麻煩可能沒有,但小麻煩一定是不斷的。

  2.  業務程式碼中,直接呼叫了具體訊息佇列的產品,當某一個模組訊息量快速上升,無法區域性切換佇列產品。(當然你也可以切分出獨立的服務,但是耗時,代價相對較大)。

  3.  對於同一解決方案內的非同步訊息,或多或少的會出現生產訊息呼叫和消費訊息呼叫程式碼分散(比如使用者登入後新增日誌)。

  4. 當前的專案程式碼依賴外部訊息佇列或者資料庫(如果公司偏專案型,新專案無法輕裝上陣)

 

  以上臆測僅個人偏見,僅供參考。在我的角度,我希望在業務呼叫訊息中轉的過程中,需要面向的是介面,在需要的時候適配即可,所以我嘗試新增一個輕簡的中間層。

  這個中間層第一件事就是隔離,草圖設計如下:

  通過全域性 DataFlowFactory 能夠建立訊息釋出者(IDataPublisher),並能夠注入訂閱者(IDataSubscriber) ,業務層只需要通過 IDataPublisher,IDataSubscriber 介面互動,和具體訊息儲存設施脫離。順著這個思路,當業務需要生產寫入訊息時,建立釋出者,並通過釋出者寫入訊息,並完成訂閱者的回撥,這個環路即可完成。

  現在只需要解決兩個問題:

  1. 建立的釋出者,如何實現不同場景的擴充套件。

  2.  如何完成對應的訂閱者(支援同一訊息型別,多個訂閱者)的回撥。

  這裡我引入一個全域性管理物件(DataFlowManager),內部的呼叫過程圖示如下:

 

  在 DataFlowManager 中提供了  PublisherProvider 公開屬性,可用來擴充套件不同訊息設施的釋出者實現。同時,提供了 NotifySubscriber 方法,作為已註冊訊息訂閱者的統一觸發入口(內部完成了多個訂閱者的排程,當然如果針對特殊消費者呼叫,使用者也可以跳過註冊訂閱者,自由實現訂閱處理)。

  通過上邊的整個過程,完全實現了訊息中間層的功能,以外掛的形式將具體的訊息設施在程式的全域性入口注入,這樣就可以針對不同環境不同業務模組(入口的引數 source_name 控制)做定製化。同時,在大多數的專案中(包括在開發環境中)並無需立即使用獨立的訊息設施,所以在中介軟體的內部,提供了一個預設的記憶體訊息佇列實現,這樣也保證了類庫的即引即用,擴充套件後的圖示如下:

  根據上邊的過程圖可以看出,內部的預設佇列,和外部佇列所處統一層級,當沒有提供使用者自定義 PublisherProvider時(或者Provider 返回的 DataPublisher為空),系統會執行內部的預設佇列實現。

二. 具體使用示例

  上邊展示了內部設計,這裡介紹具體的程式碼使用層面,看在實際的使用中是如何簡化內斂整個訊息的處理。

  1.  業務側呼叫:

   上邊演示了訊息Key為 “ P-S-Msg ” ,有兩個訂閱者(這裡使用了委託方法,也可以傳入繼承IDataSubscriber<MsgData> 介面的例項),並且建立了一個名為 _publisher 的釋出者(如果沒有註冊其他訊息儲存適配實現,會走預設內部訊息佇列實現,即建立一個名為 NewSource 的佇列)。除了上邊的使用方式,有些時候我們的生產和消費程式碼都是同一個服務內部,比如使用者登入和新增登入日誌,這個時候提供了一個更簡單的方式:

   可以看出,具體的業務程式碼相對很清晰,不需要關注具體的訊息底層實現,或者什麼觸發方式(定時,webhook或者內部消費執行緒)。而這些內容全部轉移到全域性模組外掛化適配。

二. 訊息的底層適配:

 

   上邊的程式碼,自定義了一個 CustomMsgStorage 訊息儲存介面卡,並在全域性初始化時,賦值給  DataFlowManager.PublisherProvider ,在這個介面卡裡,約定了當  source_name 等於 CustomStorageQueue 時返回訊息釋出者 CustomMsgStoragePublisher,當然這個具體實現可以替換成 RabbitMQ,Redis,Mysql 等等,當訊息實際消費觸發時,呼叫 DataFlowManager.NotifySubscriber() 方法即可,在上邊的測試用例裡,我簡化了這個過程,直接呼叫,實際場景根據情況調整即可(比如放在RabbitMQ的消費監控執行緒,或者讀取Mysql資料的定時任務中)。

  簡單來說,DataFlowFactory處理業務使用介面(通過 msgkey 關聯生產者和消費訂閱者),DataFlowManager 控制具體的訊息適配(通過 source_name 來控制底層適配), 基本解決了前面我所顧慮的問題。

如果你已經看到這裡,並且感覺還行的話可以在下方點個贊,或者也可以關注我的公總號(見二維碼)


 

相關文章