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


目錄

  1. .NET-記一次架構優化實戰與方案-梳理篇

  2. .NET-記一次架構優化實戰與方案-前端優化

  3. .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

結束

  本系列到此基本上分享完了,如果大家有更加好的意見,可在下方評論反饋給我。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM