導語

本文針對業務中異常場景處理問題,介紹了基於分佈式調度機制的通用重試方案,該方案保證了業務的最終一致性,且大大降低了人工恢復異常數據的成本。。

背景

在分佈式環境下,我們大部分的系統都不是單獨存在的,系統與系統之間的相互依賴已是常態。

從宏觀角度看,分佈式“依賴”即增加了系統的整體複雜度,也增加了系統出現問題的概率。例如:在做下單操作時,調用支付系統成功,但是最終添加商品超時;當業務系統依賴的下游服務出現嚴重的系統問題時,導致在一段時間內的業務受到影響。系統問題最終都會體現到數據一致性上。而在解決一致性問題上,前輩們已經總結出通用的解決方案:分佈式式一致性算法(paxos、兩階段提交、三階段提交等)、最終一致性(重試、補償、TCC)、對賬。

採用分佈式一致性算法去解決分佈式一致性問題雖然是比較徹底的解決方案,但是實現太過複雜,並且根據CAP理論,這種方式會降低系統整體可用性。因此在我們的業務系統中,大部分都依賴最終一致性方案去解決分佈式一致性問題。

“重試”是比較常用的最終一致性方案:

  • “三次重試”:是最直接的方式,能減少偶發性的網絡故障帶來的異常。但是對於極端情況,三次重試不僅無效,而且還會對下游系統造成更大壓力。

  • 通過重試隊列實現梯度重試:使用這種方式會根據重試次數的增加去延長下次重試時間(隨着重試次數增加,但是重試依然不成功,說明目標系統恢復時間比較長,因此可以根據重試次數延長下次重試時間)。這種方式即能有效提高重試成功的幾率,也能通過柔性化的重試避免對下游系統造成更大壓力。但是實現起來比較複雜,成本比較高。

  • 人工修復:人工修復也是重試,只不過需要人工操作。當走到這一步時,說明已經造成了線上事故,我們通常的處理方式就是:篩選日誌->開發修復腳本->修復腳本測試->批量修復。這個流程已經相當於一個小型的需求開發,我們需要花費至少1天時間去修復。如果業務邏輯複雜,系統日誌不全,需要花費更多的時間去修復。

相信做過後端開發的同學對於上述方案的痛點感同身受。因此我們開發了基於分佈式調度的分佈式重試系統。系統提供了多樣化的重試策略;能自動探測目標系統的恢復情況,並進行數據的自動恢復;提供了可視化的操作平臺,降低了人工修復數據的成本;並提供了基於異常點的監控報警機制,提高問題定位的效率。

整體介紹

整體架構

1、概念介紹:

集羣:即接入方系統的集羣標識。
重試點:重試執行的入口,重試點可根據業務自定義重試策略(重試次數、重試間隔、重試數據有效期等)。
節點:代表集羣中的某個機器IP。

2、模塊介紹:

A、重試客戶端:通過jar包形式提供給業務方統一接入:

  • 上報數據:提供了統一的數據上報方式,利用公司的MQ組件進行數據的上報。

  • 重試點執行:基於jetty server的形式封裝了RPC服務,接收重試中心的調用請求,通過重試點作爲入口進行重試邏輯的執行。

  • 心跳保持:心跳保持模塊會每隔10s向註冊管理模塊發起節點註冊。

B、註冊管理:負責集羣、重試點、客戶端節點和報警配置的註冊管理。

C、重試中心:負責重試策略的統一控制和調度:

  • 數據收集:負責接收重試客戶端的數據上報。

  • 事件監聽:負責監聽重試事件,觸發重試執行,比如手動重試觸發、業務方根據系統恢復情況進行重試觸發。

  • 自動重試:根據重試點設置的重試策略進行規律型的重試執行,比如:固定間隔時間重試、梯度間隔重試、用戶自定義間隔重試。

  • 限流熔斷:限流會根據集羣和重試點設置的QPS閾值進行重試調度的速率,避免在極端情況對業務系統造成壓力;熔斷是根據重試調用客戶端反饋的結果進行統計,如果反饋結果的失敗率達到設定的閾值,則說明業務系統段時間不可恢復,則進行重試中斷,並將重試點交給探測恢復模塊進行自動恢復。

D、監控報警:根據報警配置,對重試隊列進行監控報警,報警信息包括:集羣、重試點(可對應到異常發生的地方)、隊列數據量。

詳細介紹

1、重試點

對於重試,就是從什麼地方發生異常,就從什麼地方開始重試,因此“重試點”其實就可以認爲是異常發生的地方。假設有如下的業務流程:

重試點拆分

他如果在B、C容易出現異常,則可以抽象出兩個重試點:重試點1執行B->C->D,重試點2執行C->D。

2、從異常發生到重試執行

重試客戶端與重試中心交互圖 

  • 當異常發生時,在異常點處上報重試數據;

  • 重試中心將接收到的數據寫入到重試隊列中;

  • 自動重試掃描模塊會掃描出重試隊列需要重試的數據進行重試執行,如果執行成功,則數據出隊,如果重試失敗,則更新下次重試時間;

  • 在自動重試時,會調用熔斷器,如果熔斷器反饋是false,則將重試點加入探測隊列,標記重試點爲探測狀態;

  • 熔斷器會異步統計最近一段時間內的執行日誌,如果失敗率達到一定的閾值,則說明目標系統短時間內不可恢復,所以中斷當前的自動重試,並將該重試點加入探測隊列;

  • 探測恢復模塊會自動去使用少量數據去進行重試的探測,如果反饋結果全部成功,則將該探測數據從探測隊列出隊,並標記重試點狀態爲自動重試狀態;

  • 重試執行通過異步分發執行,並會調用限流器進行重試點維度的限流控制;在對單條數據進行調度時,會通過路由器通過輪詢方式篩選出調用的目標節點,當調度時,發現目標節點失效,則會輪詢選擇下個節點進行調用;

  • 重試客戶端的jetty server收到調用請求時,會選擇一個worker執行請求;worker會通過調度的信息找到目標的重試點進行重試業務邏輯的執行:執行成功,返回SUCCESS,失敗返回返回FAIL。

3、重試數據的可靠性重試策略

  • 重試執行時,避免當前重試點的數據上報:

校驗是否從重試點進行調用 

  • 使用公司自研MQ組件wmb進行重試數據上報,send and receive ok 提高發送的可靠性,重試中心消費消息進行pull and ack機制進行消費,保證消費的可靠性;在重試數據上報時,會帶一個uuid,作爲重試數據入隊的冪等性校驗的依據。

4、重試策略

4.1、支持多樣化的重試策略

常見的重試方式有固定間隔事件重試、梯度重試、或者由人工等外部因素髮起重試。因此重試的觸發包括兩種:事件觸發和自動觸發。

  • 事件觸發可以由用戶手動操作或業務系統自定義發起重試操作;

  • 自動觸發是由系統自動發起,主要分爲三個階段:重試點讀取、重試數據掃描和重試調度:

自動觸發基本流程

A、重試點讀取是分頁讀取,會讀取狀態(retry point state)爲正常狀態的重試點數據;

B、掃描前,會嘗試獲取該重試點粒度的分佈式鎖,如果獲取成功,則進行步驟c掃描重試數據;如果獲取失敗,說明該重試點在其他節點正在執行,則繼續遍歷下一個重試點;
C、重試數據掃描階段會掃描重試數據狀態(retry data state)爲未執行狀態並且下次執行時間(nexttime)小於等於當前時間的數據,只取前5000條數據,避免單節點執行的壓力;掃描出的數據會標識爲執行中狀態,並異步進入重試執行階段;遍歷完成後,釋放鎖;
D、異步執行階段,主要是記錄執行日誌,發起RPC調用,並根據執行結果進行數據狀態變更:如果重試成功,則當前數據出隊;如果重試失敗,則校驗數據重試次數是否達到上限,或數據是否過期,如果是,則淘汰數據到觸發重試隊列,否則更新當前數據的下次重試時間(nexttime)、重試次數(retry count)加1和狀態(retry data state)標誌爲未執行;

  • 自動重試時,下次重試時間(nexttime)的計算:

    當重試次數n=0時,說明是首次重試,會立即觸發重試;

    定義函數interval=f(n),值域∈N+∩(0,expire],定義域n∈N+∩[1,maxNum]

A、interval代表下次重試時間離當前時間(currentTime)差;

B、n代表重試次數;

C、expire代表數據有效期;

D、maxNum代表最大重試次數;

E、c代表每次重試的間隔時間c>=1∩c∈N+;

因此可定義下次的重試時間點nextRetryTime= f(n) + currentTime;

A、常量法:Interval=f(n)=c,代表固定間隔時間的重試策略;

B、函數法,代表梯度間隔時間的重試策略:

a)、f(n)=cn;

每次的間隔時間均勻增加;

b)、Interval=f(n)=c*2^n

每次的間隔時間增加爲c的2^n倍;

C、自定義時間點法:

Interval=f(n)={1,3,5,10,15};

自定義法的重試次數爲數組長度length+1次,即n∈[1,length+1]。

4.2、重試限速

  • 背景:在極端情況下,如果出現大規模的重試,則會對目標系統或目標系統的下游系統造成極大的壓力。因此在重試時,需要進行限速。

  • 實現:

重試限速的實現

  • 限流是根據設定的限流閾值進行限制,採用zookeeper的動態節點監聽+單機令牌桶算法實現了僞分佈式限流,單機QPS=總QPS/節點數。

4.3、智能化重試

  • 背景:由於目標系統的恢復時間不可預知,通過規律的重試後(固定間隔重試、梯度重試等),依然不能重試成功,這樣即造成了大量無用的重試(重試成功率過低),也最終要依賴人工恢復。因此我們實現了相對智能化的重試策略。

  • 分析:由於我們採用的是分佈式調度模型,因此在重試中心很容易能監控到每個重試點執行日誌。我們根據日誌,通過統計分析模型就可以分析出重試目標系統的健康狀況,這樣我們可以通過統計分析結果,做出相應的熔斷和探測恢復,實現智能化重試。

  • 實現:

智能重試實現

A、熔斷:對重試日誌進行統計分析,並能分析出重試目標系統的健康狀態(失敗率達到一定閾值)。熔斷後的重試點標記探測狀態,並加入探測隊列。
B、探測恢復:根據統計分析模型去分析目標系統的恢復情況:系統會自動的使用少量的數據去嘗試重試,如果反饋成功率是100%,則可以判斷目標系統已經恢復正常,此時將該重試點出隊,並標記該重試點狀態爲正常狀態。

總結

分佈式重試系統其實是一個“合併同類項”後的成果,重試的思路都來源於日常的總結,基本適用於大多數的重試場景。分佈式重試系統可以是對業務系統的一種自我恢復能力的補充,能提高業務系統的穩定性。當出現系統問題時,如果通過重試系統進行自動恢復,那就不會造成事故。對於需要人工確認的異常數據,提供了可視化的操作界面,降低了處理問題的成本。

分佈式重試系統除了提高系統自我恢復能力,也可基於重試點去進行精細化監控報警。重試點即能基本對應到異常發生的地方,因此可以提高問題排查的效率。針對重試數據統計分析,我們也可以對業務系統的健康度做一些評估,爲業務系統迭代優化提供依據。

分佈式系統並沒有做到很完美,“重試點”的切分還是需要依賴人工去做,這樣很容易會有所疏漏。如果能通過一種技術方案,系統自動識別到系統“重試點”,並自動生成重試業務邏輯代碼,那就更加完美了。大家如果有好的想法歡迎能一起交流溝通,只有思想不斷的碰撞,才能產生更完美的方案。

作者簡介

馬文斌,營銷技術團隊後端開發工程師。參與過業務系統的重構、營銷平臺的搭建,負責設計開發了分佈式調度平臺和分佈式重試調度系統等橫向技術項目。

相關文章