1. 程式人生 > >微信後端服務架構及其過載控制系統DAGOR

微信後端服務架構及其過載控制系統DAGOR

微信架構介紹

 

眼下的微信後端包含3000多個移動服務,包括即時訊息、社交網路、移動支付和第三方授權。該平臺每天收到的外部請求在10 ^10個至10^11個。每個這樣的請求都會觸發多得多的內部微服務請求,因而微信後端整體每秒需要處理數億個請求。

 

微信的微服務系統容納在微信業務系統中的20000多臺機器上執行著3000多個服務,隨著微信變得極其流行,這些數字在不斷增加......隨著微信不斷髮展,其微服務系統一直在經歷服務更新的快速迭代。比如說,從2018年3月到5月,微信的微服務系統平均每天要經歷近千次變更。

 

微信將其微服務分成這幾大類:“Entry leap”服務(接收外部請求的前端服務)、“Shared leap”服務(中間層編排服務)和“基本服務”(不向任何其他服務敞開的服務,因而充當請求的接收器)。

 

微信的微服務架構

 

在平常的一天,峰值請求率是每日平均值的3倍左右。在每年的某些時段(比如農曆新年前後),峰值工作負載會激增至每日平均值的10倍。

 

為基於微服務的大規模平臺設計過載控制系統面臨的挑戰

 

過載控制......對於儘管面臨任何不可預測的負載激增情形,仍需要實施24×7服務可用性的大規模線上應用而言至關重要。

 

傳統的過載控制機制是為擁有少量服務元件、比較窄的“前門”(front-door)和平凡依賴項的這種環境設計的。

 

……現代線上服務在架構和依賴項方面正變得越來越複雜,遠遠超出了傳統過載控制的設計用途。

 

  • 由於對傳送到微信後端的服務請求而言沒有單一的入口點,在全域性入口點(閘道器)實行集中式負載監控這種常規方法並不適用。

  • 某個特定請求的服務呼叫圖可能依賴針對特定請求的資料和服務引數,即便對於同一型別的請求也是如此。因此,某個特定服務變得過載時,很難確定應丟棄哪些型別的請求以緩解這種情況。

  • 過多的請求中止(處於呼叫圖的更深處或請求處理的更晚期時尤為如此)浪費了計算資源,並因高延遲而影響了使用者體驗。

  • 由於服務DAG極其複雜且不斷髮展,因此有效的跨服務協調所需的維護成本和系統開銷過高。

 

由於一個服務可能對它依賴的某個服務發出多個請求,還可能對多個後端服務發出請求,因此我們在過載控制方面格外小心。論文作者為多個過載服務被呼叫或單單一個過載服務被多次呼叫的情況首次提出了後續過載(subsequent overload)這個術語。

 

後續過載給有效的過載控制提出了挑戰。直觀上來講,服務過載時隨機地執行減載(load shedding)可以使系統經受得住飽和的吞吐量。然而,後續過載可能使系統吞吐量大大降低,超出預期......

 

考慮一個簡單的場景:服務A兩次呼叫服務B。如果B開始拒絕所有入站請求中的一半,A的成功率就降至0.25。

 

DAGOR概述

 

微信的過載控制系統名為DAGOR,它旨在為所有服務提供過載控制,因而旨在與服務無關。由於集中式全域性協調的成本過於高昂,過載控制在單臺機器的細粒度層面來執行。然而,它確實包含一種輕量級協作式機器間協議,處理後續過載情況需要該協議。最後,因過載而導致減載不可避免時,DAGOR會維持服務的盡力(best-effort)成功率。應儘量減少耗費在失敗的服務任務上的計算資源(比如CPU和I/O)。

 

我們有兩個基本任務要解決:檢測過載情況,檢測出來後決定如何處理。

 

  • 過載檢測

 

針對過載檢測,DAGOR使用了待處理佇列中請求的平均等待時間(即佇列時間)。佇列時間的優點在於抵消了呼叫關係圖(call-graph)低層次中的延遲帶來的影響(與比如請求處理時間相比)。即使本地伺服器本身沒有過載,請求處理時間也會增加。DAGOR使用基於視窗的監控,一個視窗對應一秒或2000個請求,先到先處理。微信顯然實行了嚴格的管理:

 

針對過載檢測,鑑於微信中每個服務任務的預設超時為500ms,表明伺服器過載的平均請求佇列時間的閾值被設定為20ms。這種單憑經驗的配置已經在微信業務系統中用了五年多,微信業務活動方面的系統穩健性已證實了其有效性。

 

  • 准入控制

 

一旦檢測到過載,我們就必須決定如何處理它。或者更直言不諱地說,我們要丟棄哪些請求。第一個結論是,並非所有請求都一樣(重要):

 

微信的操作日誌顯示,微信支付和即時訊息遇到類似的服務不可用時段時,使用者對微信支付服務的投訴是對即時訊息服務的投訴的100倍。

 

為了以一種與服務無關的方式處理這個問題,每個請求在首次進入系統時都被賦予業務優先順序。該優先順序與所有下游請求一塊傳送。使用者請求的業務優先順序由請求的操作型別決定。雖然有數百個入口點,但只有幾十個有顯式優先順序,其他所有入口點都有預設(較低)優先順序。優先順序保留在一張複製的雜湊表中。

 

雜湊表儲存了在微信入口服務中執行的操作的業務優先順序

 

過載控制被設定為業務優先順序n時,來自優先順序n+1的所有請求一律被丟棄。這對於混合工作負載來說很好,但是假設有潮水般湧入的支付請求,所有這些請求都有同樣的優先順序(比如p)。系統將變得過載,因此將過載閾值改為p-1,這時系統將再次處於輕負載情形。一旦檢測到輕負載,過載閾值再次調高到p,再一次處於過載狀態。為了在因同一優先順序的請求而超載時停止這種翻轉,我們需要比業務優先順序更精細的粒度級別。

 

微信有一個很好的辦法來解決這個問題。它增加了基於使用者ID的第二層准入控制。

 

使用者優先順序由入口服務通過以使用者ID作為引數的雜湊函式動態生成。每個入口服務每小時更改其雜湊函式。因此,來自同一使用者的請求很可能在一小時內被賦予同樣的使用者優先順序,但幾小時內被賦予不同的使用者優先順序。

 

這提供了公平性,同時又讓每個使用者在一段比較長的時間內提供了一致的體驗。它還有助於解決後續過載問題,因為來自被賦予高優先順序的使用者的請求更可能在呼叫圖中一路得到優先處理。

 

結合業務優先順序和使用者優先順序提供了複合准入級別,每個業務優先順序有128個使用者優先順序。

 

複合准入級別

 

由於業務優先順序的每個准入級別有128個使用者優先順序,因此所得的複合准入級別數量是成千上萬。可在使用者優先順序這個層面調整複合准入級別。

 

詳細的補充內容解釋了為什麼使用會話ID而不是使用者ID不行:你到頭來訓練使用者在遇到糟糕的服務時先登出再重新登入,現在除了原來的過載問題,你還多了登入風暴(login storm)!

 

DAGOR在每臺伺服器上都有一張請求直方圖,跟蹤請求相對準入優先順序的大致分佈。視窗期間檢測到過載時,DAGOR進入到將預期負載減少5%的第一個桶(bucket)。由於沒有過載,它進入到將預期負載增加1%的第一個桶。

 

伺服器將當前的准入級別捎帶在傳送到上游伺服器的每個響應訊息上。這樣一來,上游伺服器知道下游服務的當前准入控制設定,甚至在傳送之前就對請求執行本地准入控制。

 

因此,端到端的DAGOR過載控制系統如下所示:

 

DAGOR過載控制的工作流程

 

試驗結果

 

DAGOR這種設計的最佳證明是,五年來它在微信的生產環境中一直順暢地運作。不過這沒有為學術論文提供必要的圖表,於是我們還進行了一組模擬試驗。下圖表明瞭基於佇列時間而不是基於響應時間的過載控制具有的好處。這些好處在後續過載的情形下體現得最為明顯,見圖(b)。

 

用不同的負載分析指標:佇列時間vs響應時間來檢測過載

 

與CoDel和SEDA相比,進行一次後續呼叫時,DAGOR在後續過載方面的請求成功率提高了50%。後續請求數量越多,好處越明顯:

 

面對不同型別的工作負載下的過載控制

 

最後在公平性方面,可以認為CoDel在負載很大時優先處理向過載服務敞開較小的服務,而DAGOR面對各種請求時表現出了大致相同的成功率。

 

過載控制的公平性

 

可用於你自家系統的三個經驗

 

即使你沒有完全按描述的那樣使用DAGOR,論文作者還是列出了三個值得考慮的寶貴經驗:

 

  • 大規模微服務架構中的過載控制面對每個服務時必須是分散的、自主的。

  • 過載控制應考慮各種反饋機制(比如DAGOR的協作式准入控制),而不是僅僅依賴於開環啟發式方法。

  • 應通過分析實際工作負載的處理行為,為過載控制設計提供事實依據。