歡迎光臨
每天分享高質量文章

.NET-記一次架構最佳化實戰與方案-底層服務最佳化

前言

  經過上一篇《.NET-記一次架構最佳化實戰與方案-前端最佳化》與大家分享了對頁面載入最佳化的心得和經歷。雖然最佳化前端的效能效率,但是由於底層服務的觸發方式,根本性問題仍然存在的。

問題分析

  在本系列第一篇文章我們提到,底層服務是一系列的JOB,那麼問題主要存在以下兩點:

  • 程式碼冗餘
  • 時效低

程式碼冗餘

例如:

  • 領獎方法不統一,一次性的寫一套,可迴圈的又寫一套。

  • 每個型別任務都需要獨自的實現該任務的完成任務 JOB 與發放獎勵 JOB

  

  以上問題直接導致了後續開發、日常維護成本過高。

  因為早期開發時缺少溝通,沒有封裝成公共的方法,而JOB每個開發人員都單獨實現了一套,當然他們未必那麼蠢,可能是某個先完成了,後續的先前COPY後修改一下。

  試想一下,沒新增一個任務型別就要重寫一份完成任務的JOB和自動發獎的JOB,這是一個N*2的工作量。如果後續有個規則改了,那是不是每個JOB都跑去改一次?

時效低

  由於任務完成是由定時服務根據業務資料源定時批次執行:

  1. 定時任務頻率低,則導致資料集中過多處理
  2. 定時任務頻率高,則導致 Job 對資料庫的壓力劇增或者 99%的無用查詢,無結果並不代表不會造成消耗,因為查詢都是要經過下麵步驟,建立連線、詞法分析、語法分析、選取執行方式、到儲存引擎讀取資料、傳回客戶端。
  3. 隨著資料源資料量增加,查詢耗時也逐漸增加

  以上問題直接導致了,使用者完成任務後無法及時檢視完成任務並領獎,如需及時檢視狀態需要在展示頁面邏輯額外新增查詢後更新的操作

最佳化實施

流程圖

方案一(抽離公共點)

目的:減少程式碼冗餘,提高可維護性,提高後續新任務的開發效率

具體實施:從業務流程圖可以直觀的觀察出,整個底層業務流程基本一致,只有資料源上的差異,因此可以從以下方面入手最佳化:

  1. 抽離出唯一的自動發獎 Job,發獎JOB基於【任務完成結果】進行發獎,逐步去除原個性任務自動發獎 Job。
  2. 自動發獎與手動領獎的具體執行流程一致的,可將其封裝成公共方法。分別由H5領獎按鈕與領獎JOB進行呼叫。
  3. 任務完成 Job 使用模板樣式,由基類統一業務執行流程,每個任務型別只需繼承任務父類,再由子類重寫查詢資料源。當然也可以簡單粗暴點不使用設計樣式,把查詢資料源後的完成任務的方法封裝成一個公共方法供不同任務型別的JOB去呼叫。

從我角度來看,這種方案處理沒有任何壞處的。如果真要計較,那就是所涉及的JOB都得改,但是從上述分析出,如果真要重構,工作量也是在寫父類模板和封裝公共方法,查詢資料原始碼是可以復用的。但是帶來的收益就是良好的擴充套件性和可維護性。

方案二(業務埋點)

該方案主要對任務參與的觸發方式變更,不同的任務型別由對應的業務最終流程的完成點進行傳送佇列訊息,由任務服務(消費端)訂閱相關訊息執行任務完成流程。

通俗講叫業務埋點,當然也可以稱其為事件驅動。

架構圖

事件驅動架構

  服務之間是高內聚的,它們的耦合度應該很低,當服務需要相互協作時,假設服務“A”需要觸發服務“B”中的某段邏輯,平常的方式是讓服務A直接序列呼叫服務B中的某個方法。但前提是A必須知道B的存在,如果B出異常了就會影響到A的正常執行。

這樣它們之間就是強耦合的,A必須依賴於B了。這樣使得系統更難以維護與擴充套件,因此引入事件驅動來降低服務間的耦合度

  當服務A需要觸發服務B的邏輯時,不要直接呼叫它,我們可以將訊息傳送到訊息佇列,由服務B訂閱相應的佇列,併在事件發生時非同步執行操作。這意味著服務A、B都依賴於中介軟體訊息佇列,但他們之間將不需要知道彼此的存在,因此它們之間於此解耦。

如果將此方案引入我們的活動業務中,收益主要分為短期與長期。

短期收入

  1.  減少無用重覆的查詢:無需重覆查詢資料源,只需由業務端推送可靠的訊息,減少對資料庫的多餘壓力
  2. 使用者體驗良好:時效性高,原集中時間點批次處理,現分散到不同的時間點執行
  3. 伸縮性優秀:RabbitMQ 自帶負載均衡功能,在消費能力不足情況下,可以做到業務無損的動態橫向擴充套件。雖然JOB也可以做到,但是需要對物理表做特殊處理,增加中間狀態

長期收益

  事件驅動架構長期收益比短期要大,以RabbitMQ與投資業務舉個例子。初期完成核心業務投資理財,投資後我們需要APP通知使用者,在此投資無論成功與否都往RabbitMQ傳送訊息,投資成功RouteKey=TZ.SUCCESS,投資失敗RouteKey=TZ.FAILE。APP通知服務訂閱佇列NoticeQueue系結RouteKey=TZ.#,其中包括成功和失敗的訊息,服務根據訊息狀態傳送APP通知。哪天業務拓展需要增加投資成功積分,只需要新增積分服務訂閱佇列IntegrationQueue並系結RouteKey=TZ.SUCCESS訊息即可。接著又多了任務活動、信用消費等,如此類推。

  由此可見,如同廣播一樣,我不知道你們誰要,如果你們需要的就好好監聽著,不需要就當耳旁風。

複雜點

分散式事務

  既然我們使用了RabbitMQ中介軟體,那麼分散式事務會選擇基於可靠訊息的方案:

  1. 訊息可靠性:保證業務端的本地事務執行成功的同時也保證佇列訊息正常釋出
  2. 訊息補償:保證訊息消費端的正常消費,如果消費失敗後需重新投遞,如果重新投遞失敗可由補償服務補償傳送。
  3. 冪等處理:因存在自動重試機制,避免重覆執行業務導致的意外問題。

模型圖

  

  這種基於可靠訊息的方案,也叫本地訊息事務表的方案,可根據自己情況自研解決,也可使用類似開源分散式事務框架 CAP 解決。https://github.com/dotnetcore/CAP

結束

贊(0)

分享創造快樂