微信團隊分享:微信後臺在海量併發請求下是如何做到不崩潰的

JackJiang發表於2022-06-15

本文引用了文章“月活 12.8 億的微信是如何防止崩潰的?”和論文“Overload Control for Scaling WeChat Microservices”的內容,有大量改動、優化和修訂。

1、引言

微信是一款國民級的即時通訊IM應用,月活使用者早就超過10億,而且經常過年過節會遇到聊天訊息量暴增的情況,服務是很容易出現過載的,但事實是微信的後臺服務一直比較穩定,那麼他們是怎麼做到的呢?

本文以微信發表的論文《Overload Control for Scaling Wechat Microservices》 為基礎(論文PDF原文下載見文末附件),分享了微信基於大規模微服務架構的後臺過載管控和保護策略,以及微信根據IM業務特點的一些獨特的架構設計做法,其中很多方法很有借鑑意義,值得一讀。

(本文已同步釋出於:http://www.52im.net/thread-39...

2、微信所面臨的併發壓力

截止論文《Overload Control for Scaling Wechat Microservices》發表前,微信後端有超過3000多個服務(包括即時聊天、社交關係、移動支付和第三方授權等),佔用20000多臺機器(隨著微信的廣泛普及,這些數字仍在不斷增加)。

面向前端請求的入口服務每天需要處理10億到100億級別的請求,而每個這樣的請求還會觸發更多內部的關聯服務,從整體來看,微信後端需要每秒處理數億個請求。

隨著微信的不斷髮展,這些服務子系統一直在快速進行更新迭代。以2018 年的3月到5月為例,在短短的兩個月時間裡,微信的各服務子系統平均每天發生近千次的變更,運維壓力可想而之。

另外:微信每天請求量的分佈很不平均,高峰期請求量能達到平時的3倍。而在特殊日子裡(比如過年的時候),高峰期的流量能飆升到平時的10倍。有時朋友圈裡有什麼刷屏的活動,流量肯定也會突增。由此可見,微信後端系統的併發壓力相當之大。

而且:微信後端的這些服務所處的環境也是不斷變化的,包括硬體故障、程式碼bug、系統變更等,都會導致服務可承受的容量動態變化。

3、微信的後端服務架構

微信後端採用的也是微服務架構。說是微服務,其實我理解就是採用統一的 RPC 框架搭建的一個個獨立的服務,服務之間互相呼叫,實現各種各樣的功能,這也是現代服務的基本架構。畢竟誰也不希望看到我朋友圈崩了,導致跟我聊天也不行了,這也是微信的典型好處。

微信後端的微服務架構一般分為3層:

如上圖所示,這3層服務分別是:

1)“入口跳板”服務(接收外部請求的前端服務);
2)“共享跳板”服務(中間層協調服務);
3)“基礎服務”(不再向其他服務發出請求的服務,也就是充當請求的接收器)。

微信後端的大多數服務屬於“共享跳板”服務,“入口跳板”服務比如登入、傳送聊天訊息、支付服務等。“基礎服務”也就是日常最好理解的這些資訊資料介面類,比如賬戶資料、個人資訊、好友/聯絡人資訊等。

按照微信後端服務的請求量(每日在十億到百億之間),入口協議觸發對“共享跳板”服務和“基礎服務”更多的請求,核心服務每秒要處理上億次的請求,也就是顯而易見的了。

4、什麼是過載保護

1)什麼是服務過載?

服務過載就是服務的請求量超過服務所能承受的最大值,從而導致伺服器負載過高,響應延遲加大。

使用者側表現就是無法載入或者載入緩慢,這會引起使用者進一步的重試,服務一直在處理過去的無效請求,導致有效請求跌 0,甚至導致整個系統產生雪崩。

2)為什麼會發生服務過載?

網際網路天生就會有突發流量、秒殺、搶購、突發大事件、節日甚至惡意攻擊等,都會造成服務承受平時數倍的壓力,比如微博經常出現某明星官宣結婚或者離婚導致伺服器崩潰的場景,這就是服務過載。

3)過載保護的好處

過載保護主要是為了提升使用者體驗,保障服務質量,在發生突發流量時仍然能夠提供一部分服務能力,而不是整個系統癱瘓。

系統癱瘓就意味著使用者流失、口碑變差、夫妻吵架,甚至威脅生命安全(假如騰訊文件崩潰,這個文件正好用於救災)。

而微信團隊在面對這種量級的高併發請求挑戰,做法是精細化的服務過載控制。我們繼續往下學習。

5、微信面臨的過載控制技術挑戰

過載控制對於大規模線上應用程式來說至關重要,這些應用程式需要在不可預測的負載激增的情況下實現 24×7 服務可用性。

傳統的過載控制機制是為具有少量服務元件、相對狹窄的“前門”和普通依賴關係的系統而設計的。

而微信這種現代即時通訊im應用的全時線上服務特性,在架構和依賴性方面正變得越來越複雜,遠遠超出了傳統過載控制的設計目標。

這些技術痛點包括:

1)由於傳送到微信後端的服務請求沒有單一的入口點,因此傳統的全域性入口點(閘道器)集中負載監控方法並不適用;
2)特定請求的服務呼叫圖可能依賴於特定於請求的資料和服務引數,即使對於相同型別的請求也是如此(因此,當特定服務出現過載時,很難確定應該限制哪些型別的請求以緩解這種情況);
3)過多的請求中止浪費了計算資源,並由於高延遲而影響了使用者體驗;
4)由於服務的呼叫鏈極其複雜,而且在不斷演化,導致有效的跨服務協調的維護成本和系統開銷過高。

由於一個服務可能會向它所依賴的服務發出多個請求,並且還可能向多個後端服務發出請求,因此我們必須特別注意過載控制。我們使用一個專門的術語,叫作“後續過載”,用於描述呼叫多個過載服務或多次呼叫單個過載服務的情況。

“後續過載”給有效的過載控制帶來了挑戰。當服務過載時隨機執行減載可以讓系統維持飽和的吞吐量,但後續過載可能會超預期大大降低系統吞吐量 …

即:在大規模微服務場景下,過載會變得比較複雜,如果是單體服務,一個事件只用一個請求,但微服務下,一個事件可能要請求很多的服務,任何一個服務過載失敗,就會造成其他的請求都是無效的。如下圖所示。

比如:在一個轉賬服務下,需要查詢分別兩者的卡號, 再查詢 A 時成功了,但查詢 B 失敗,對於查卡號這個事件就算失敗了。比如查詢成功率只有 50%, 那對於查詢兩者卡號這個成功率只有 50% * 50% = 25% 了, 一個事件呼叫的服務次數越多,那成功率就會越低。

6、微信的過載控制機制

微信的微服務過載控制機制叫“DAGOR”(因為微信把它的服務間關係模型叫“directed acyclic graph ”,簡稱DAG)。

顯然這種微服務底層的機制必須是和具體的業務實現無關的。DAGOR還必須是去中心化的,否則的話在微信這麼大且分佈不均的流量下,過載控制很難做到實時和準確。同時也無法適應微服務快速的功能迭代釋出(平均每天要發生近1000次的微服務上下線)。

此外,DAGOR還需要解決一個問題:服務呼叫鏈很長,如果底層服務因為過載保護丟棄了請求,上層服務耗費的資源全浪費了,而且很影響使用者體驗(想想進度條走到99%告訴你失敗了)。所以過載控制機制在各服務之間必須有協同作用,有時候需要考慮整個呼叫鏈的情況。

首先我們來看怎麼檢測到服務過載。

7、微信如何判斷過載

通常判斷過載可以使用吞吐量、延遲、CPU 使用率、丟包率、待處理請求數、請求處理事件等等。

微信使用在請求在佇列中的平均等待時間作為判斷標準。平均等待時間就是從請求到達,到開始處理的時間。

為啥不使用響應時間?因為響應時間是跟服務相關的,很多微服務是鏈式呼叫,響應時間是不可控的,也是無法標準化的,很難作為一個統一的判斷依據。

那為什麼也不使用 CPU 負載作為判斷標準呢? 因為 CPU 負載高不代表服務過載,因為一個服務請求處理及時,CPU 處於高位反而是比較良好的表現。實際上 CPU 負載高,監控服務是會告警出來,但是並不會直接進入過載處理流程。

騰訊微服務預設的超時時間是 500ms,通過計算每秒或每 2000 個請求的平均等待時間是否超過 20ms,判斷是否過載,這個 20ms 是根據微信後臺 5 年摸索出來的門檻值。

採用平均等待時間還有一個好處是:獨立於服務,可以應用於任何場景,而不用關聯於業務,可以直接在框架上進行改造。

當平均等待時間大於 20ms 時,以一定的降速因子過濾調部分請求,如果判斷平均等待時間小於 20ms,則以一定的速率提升通過率,一般採用快降慢升的策略,防止大的服務波動,整個策略相當於一個負反饋電路。

8、微信的過載控制策略

微信後臺一旦檢測到服務過載,就需要按照一定的過載保戶策略對請求進行過濾控制,來決定哪些請求能被過載服務處理,哪些是需要丟棄的。

前面我們分析過,對於鏈式呼叫的微服務場景,隨機丟棄請求會導致整體服務的成功率很低。所以請求是按照優先順序進行控制的,優先順序低的請求會優先丟棄。

那麼從哪些維度來進行優化級的分級呢?

8.1 基於業務的優先順序控制
對於微信來說,不同的業務場景優先順序是不同的, 比如:

1)登入場景是最重要的業務(不能登入一切都白瞎);
2)支付訊息比普通im聊天訊息優先順序高(因為使用者對金錢是更敏感的);
3)普通訊息又比朋友圈訊息優先順序高(必竟微信的本質還是im聊天)。

所以在微信內是天然存在業務優先順序的。

微信的做法是,預先定義好所有業務的優先順序並儲存在一個Hash Table裡:

沒有定義的業務,預設是最低優先順序。

業務優先順序在各個業務的入口服務(Entry Services)中找到請求元資訊裡。由於一個請求成功與否依賴其下游服務所有的後續請求,所以下游服務的所有後續請求也會帶上相同的業務優先順序。當服務過載時,會處理優先順序更高的請求,丟棄優先順序低的請求。

然而,只用業務優先順序決定是否丟棄請求,容易造成系統顛簸,比如:

1)支付請求突然上漲導致過載,訊息請求被丟棄;
2)丟棄訊息請求後,系統負載降低了,又開始處理訊息請求;
3)然而,處理訊息請求又導致服務過載,又會在下一個視窗拋棄訊息請求。

這樣反覆調整服務請求管制,整體體驗非常不好。所以微信需要更精細化的服務請求管制。

PS:微信嘗試過提供API讓服務提供方自己修改業務優先順序,後來在實踐中發現這種做法在不同的團隊中極難管理,且對於過載控制容易出錯,最終放棄了。

8.2 基於使用者的優先順序控制
很明顯,正如上節內容所述,只基於業務優先順序的控制是不夠的:

1)首先不可能因為負載高,丟棄或允許通過一整個業務的請求,因為每個業務的請求量很大,那一定會造成負載的大幅波動;
2)另外如果在業務中隨機丟棄請求,在過載情況下還是會導致整體成功率很低。
為了解決這個問題,微信引入使用者優先順序。

微信在每個業務優先順序內按使用者ID計算出的128個優先順序:

首先使用者優先順序也不應該相同,對於普通人來說通過 hash 使用者唯一 ID計算使用者優先順序(這個hash函式每小時變一次,讓所有使用者都有機會在相對較長的時間內享受到高優先順序,保證“公平”)。跟業務優先順序一樣,單個使用者的訪問鏈條上的優先順序總是一致的。

這裡有個疑問:為啥不採用會話 ID 計算優先順序呢?

從理論上來說採用會話 ID 和使用者 ID 效果是一樣的,但是採用會話 ID 在使用者重新登入時重新整理,這個時候可能使用者的優先順序可能變了。在過載的情況下,他可能因為提高了優先順序就恢復了。

這樣使用者會養成壞習慣,在服務有問題時就會重新登入,這樣無疑進一步加劇了服務的過載情況。

於是,因為引入了使用者優先順序,那就和業務優先順序組成了一個二維控制平面。根據負載情況,決定這臺伺服器的准入優先順序(B,U),當過來的請求業務優先順序大於 B,或者業務優先順序等於 B,但使用者優先順序高於 U 時,則通過,否則決絕。

下圖就是這個“優先順序(B,U)”控制邏輯(我們會在後面再具體討論):

8.3 自適應優先順序調整
在大規模微服務場景下,伺服器的負載變化是非常頻繁的。所以伺服器的准入優先順序是需要動態變化的,微信分了幾十個業務優先順序,每個業務優先順序下有 128 個使用者優先順序,所以總的優先順序是幾千個。

如何根據負載情況調整優先順序呢?

最簡單的方式是從右到左遍歷:每調整一次判斷下負載情況。這個時間複雜度是 O(n), 就算使用二分法,時間複雜度也為 O(logn),在數千個優先順序下,可能需要數十次調整才能確定一個合適的優先順序,每次調整好再統計優先順序,可能幾十秒都過去了,這個方法無疑是非常低效的。

微信提出了一種基於直方圖統計的方法快速調整准入優先順序:伺服器上維護者目前准入優先順序下,過去一個週期的(1s 或 2000 次請求)每個優先順序的請求量。當過載時,通過消減下一個週期的請求量來減輕負載。假設上一個週期所有優先順序的通過的請求總和是 N,下一個週期的請求量要減少 N*a,怎麼去減少呢?每提升一個優先順序就減少一定的請求量,一直提升到 減少的數目大於目標量,恢復負載使用相反的方法,只不是係數為 b ,比 a 小,也是為了快降慢升。根據經驗值 a 為 5%,b 為 1%。

為了進一步減輕過載機器的壓力,能不能在下游過載的情況下不把請求發到下游呢?否則下游還是要接受請求、解包、丟棄請求,白白的浪費頻寬,也加重了下游的負載。

為了實現這個能力:在每次請求下游服務時,下游把當前服務的准入優先順序返回給上游,上游維護下游服務的准入優先順序,如果發現請求優先順序達不到下游服務的准入門檻,直接丟棄,而不再請求下游,進一步減輕下游的壓力。

9、實驗資料

微信的這套服務過載控制策略(即DAGOR)在微信的生產環境已經運作多年,這是對它的設計可行性的最好證明。

但並沒有為學術論文提供必要的圖表,所以微信同時進行了一組模擬實驗。

下面的圖表突出顯示了基於排隊時間而非響應時間的過載控制的好處。在發生後續過載的情況下,這些好處最為明顯(圖右)。

10、小結一下

微信的整個過載控制邏輯流程如下圖所示:

針對上面這張圖,我們來解讀一下:

1)當使用者從微信發起請求,請求被路由到接入層服務,分配統一的業務和使用者優先順序,所有到下游的字請求都繼承相同的優先順序;
2)根據業務邏輯呼叫 1 個或多個下游服務,當服務收到請求,首先根據自身服務准入優先順序判斷請求是接受還是丟棄(服務本身根據負載情況週期性的調整准入優先順序);
3)當服務需要再向下游發起請求時,判斷本地記錄的下游服務准入優先順序(如果小於則丟棄,如果沒有記錄或優先順序大於記錄則向下遊發起請求);
4)下游服務返回上游服務需要的資訊,並且在資訊中攜帶自身准入優先順序;
5)上游接受到返回後解析資訊,並更新本地記錄的下游服務准入優先順序。

微信的整個過載控制策略有以下三個特點:

1)業務無關的:使用請求等待時間而不是響應時間,制定使用者和業務優先順序,這些都與業務本身無關;
2)高效且公平: 請求鏈條的優先順序是一致的,並且會定時改變 hash 函式調整使用者優先順序,過載情況下,不會總是影響固定的使用者;
3)獨立控制和聯合控制結合:准入優先順序取決於獨立的服務,但又可以聯合下游服務的情況,優化服務過載時的表現。

11、寫在最後

微信團隊的分享只提到過載控制,但我相信服務呼叫方應該還有一些其他機制,能夠解決不是因為下游服務過載,而是因為網路抖動導致的請求超時問題。

微信的這套微服務過載控制機制(即DAGOR)提供的服務無關、去中心化、高效和公平等特性很好地在微信後端跑了很多年。

最後,微信團隊還分享了他們設計和運維DAGOR寶貴經驗:

1)大規模微服務架構中的過載控制必須在每個服務中實現分散和自治;
2)過載控制應該要考慮到各種反饋機制(例如 DAGOR 的協作準入控制),而不是僅僅依賴於開環啟發式;
3)應該通過分析實際工作負載來了解過載控制設計。

12、參考資料

[1] Overload Control for Scaling WeChat Microservices
[2] 羅神解讀“Overload Control for Scaling WeChat Microservices”
[3] 2W臺伺服器、每秒數億請求,微信如何不“失控”?
[4] DAGOR:微信微服務過載控制系統
[5] 月活 12.8 億的微信是如何防止崩潰的?
[6] 微信朋友圈千億訪問量背後的技術挑戰和實踐總結
[7] QQ 18年:解密8億月活的QQ後臺服務介面隔離技術
[8] 微信後臺基於時間序的海量資料冷熱分級架構設計實踐
[9] 架構之道:3個程式設計師成就微信朋友圈日均10億釋出量[有視訊]》
[10] 快速裂變:見證微信強大後臺架構從0到1的演進歷程(一)
[11] 一份微信後臺技術架構的總結性筆記》

13、論文原文

論文PDF請下載此附件:
(因無法上傳附件,請從此連結:http://www.52im.net/thread-39...文末的“參考資料”附件中下載)

論文PDF全部內容概覽:

(本文已同步釋出於:http://www.52im.net/thread-39...

相關文章