歡迎來到啾啾的博客🐱。
記錄學習點滴。分享工作思考和實用技巧,偶爾也分享一些雜談💬。
有很多很多不足的地方,歡迎評論交流,感謝您的閱讀與評論😄。
目錄
- 反正能用的系統
- 問題分析
- 方案一:簡單多實例增加些許容錯
- 方案二:任務調度機制
- 方案三:Kubernetes+主備模式
反正能用的系統
幾年前,我曾經參與過一個月工單10億級別(TPS數萬/秒)的系統開發,當時的架構設計大致如下。
圖以數據流為導向,省去了一些微服務架構組件。
很顯然,這個架構存在一個明顯的問題:數據分區后由不同的服務專門處理,若這個專職的服務掛掉,該分區的數據在服務恢復前都將無法得到處理。
那么,我們應該怎么改善這個設計呢?
問題分析
-
主要待解決的問題
-
功能性問題
專職服務掛掉后,分區數據得不到處理。 -
拓展問題
當前的數據分割方式是在系統設計時靜態劃分。如果數據量超出設計預期,系統處理不了,也不好拓展。 -
性能問題
不能保障數據均勻地分布在各個分區,也不能保障每個專職服務處理速度一致。可能Server1處理完數據了,Server2還有一半沒有處理,服務器性能得不到充分利用。
-
-
其他問題
略…
方案一:簡單多實例增加些許容錯
每個專職服務都部署成多實例,從而提升容錯能力,解決部分功能性問題。
多實例會帶來一個問題:多個服務競爭同一批任務。
這是很簡單的資源競爭問題,可以簡單使用鎖來避免資源競爭。
在分布式架構中,使用第三方存儲即可解決,如Redis,ZooKeeper。
另外設計兜底機制,如果多個服務實例中存在不靠譜實例,競爭到了資源但是沒有完成,使用告警機制重新競爭處理。
以及從底層設計支持冪等,防止重復消費帶來問題。
- 基于Redis或Zookeeper設計任務分片獲取
關于任務:任務應該有一個唯一ID、狀態(如:待處理、處理中、已完成、失敗)、處理實例ID(可選,用于追蹤)、嘗試次數等字段。
實例需要嘗試獲取并鎖定一個“未處理”的任務批次。
因此,我們需要一種方式來標識數據分片。
如果數據本身有連續ID或者可以按某種規則分批,那是最好的。如果不行,可能需要預先在DB中標記好批次,或者有一個專門的“任務池”表。為了簡化,我們假設數據可以按ID范圍劃分。
簡單流程圖如下:
選定幾個實例負責加載任務,所有實例從任務池中獲取任務,并對任務加鎖(Redis的SETNX)。
詳細UML如下(AI生成):
但是這樣設計容錯率還是不夠、且不能無法解決其他分區的動態資源分配問題。對所有服務來說性能也沒有利用到極致。
方案二:任務調度機制
我們可以補充設計一個資源調度系統來解決所有問題。
簡單草圖如下:
很顯然,我們需要有
-
服務管理與檢測
需要獲取服務狀態以分配任務:需要知道服務實例的處理狀態,哪些服務可以分配任務,以及還能接收多少數據也會損害服務性能。 -
動態分區計算
需要支持動態擴展服務。以及最快最好分配數據處理任務到服務,充分利用資源。
原來的數據庫分庫分表結構不做更改,需要分庫分表來緩解數據查詢壓力。
調度服務需要獲取數據與分配數據。
任務(數據)獲取注意事項:需要能感知數據源數據量、數據概要信息(比如區間信息,用于分區)。獲取連續數據、內存數據、多線程獲取并匯總信息用于分配。
任務分配:要避免多個分配者競爭。
- 容錯設計
如果調度服務掛掉了怎么辦?
管理服務注冊表,服務狀態檢測。
很顯然,我們可以使用Kafka來解決上述問題。任務均發送至Kafka,由Kafka的rebalance機制進行任務調度,且Kafka消費者組可以很大程序上解決容錯問題。也較易擴展。
方案三:Kubernetes+主備模式
最后但是的團隊在一次迭代中選擇了容器化+主備模式的方式來解決容錯問題。
即,為每個專職服務設置一個備用實例。并且將服務容器化。