[點(diǎn)晴永久免費(fèi)OA]國(guó)民級(jí)應(yīng)用:微信是如何防止崩潰的?
導(dǎo)讀 微信作為月活過(guò)10億的國(guó)民級(jí)應(yīng)用,經(jīng)常面臨特殊節(jié)點(diǎn)消息量暴增的問題,服務(wù)很容易出現(xiàn)過(guò)載。但微信的服務(wù)一直比較穩(wěn)定,是如何做到的呢?本文邀請(qǐng)到了騰訊WXG后開開發(fā)工程師alexccdong以微信 2018 年發(fā)表于Socc會(huì)議上的文章《Overload Control for Scaling Wechat Microservices》 為基礎(chǔ),介紹微信大規(guī)模微服務(wù)的過(guò)載保護(hù)策略,其中很多方法很有借鑒意義。歡迎繼續(xù)閱讀。 過(guò)載保護(hù)基本概念1)什么是服務(wù)過(guò)載?服務(wù)過(guò)載就是服務(wù)的請(qǐng)求量超過(guò)服務(wù)所能承受的最大值,從而導(dǎo)致服務(wù)器負(fù)載過(guò)高,響應(yīng)延遲加大。用戶側(cè)表現(xiàn)就是無(wú)法加載或者加載緩慢。這會(huì)引起用戶進(jìn)一步的重試,服務(wù)一直在處理過(guò)去的無(wú)效請(qǐng)求,導(dǎo)致有效請(qǐng)求跌 0,甚至導(dǎo)致整個(gè)系統(tǒng)產(chǎn)生雪崩。 2)為什么會(huì)發(fā)生服務(wù)過(guò)載?互聯(lián)網(wǎng)天生就會(huì)有突發(fā)流量。秒殺、搶購(gòu)、突發(fā)大事件、節(jié)日甚至惡意攻擊等,都會(huì)造成服務(wù)承受平時(shí)數(shù)倍的壓力。微博經(jīng)常出現(xiàn)某明星官宣結(jié)婚或者離婚導(dǎo)致服務(wù)器崩潰的場(chǎng)景,這就是服務(wù)過(guò)載。 3)過(guò)載保護(hù)的好處提升用戶體驗(yàn)、保障服務(wù)質(zhì)量。在發(fā)生突發(fā)流量時(shí)仍然能夠提供一部分服務(wù)能力,而不是整個(gè)系統(tǒng)癱瘓,系統(tǒng)癱瘓就意味著用戶流失、口碑變差、夫妻吵架甚至威脅生命安全(例如提供醫(yī)療資源協(xié)調(diào)服務(wù)的app)。 微信中的過(guò)載場(chǎng)景微信采用的是微服務(wù)。微服務(wù)采用統(tǒng)一的 RPC 框架搭建一個(gè)個(gè)獨(dú)立的服務(wù),服務(wù)之間互相調(diào)用,實(shí)現(xiàn)各種各樣的功能,這也是現(xiàn)代服務(wù)的基本架構(gòu)。畢竟誰(shuí)也不想看到自己朋友圈崩掉導(dǎo)致聊天功能也無(wú)法正常使用。 微信的服務(wù)是分三層:接入服務(wù)、邏輯服務(wù)、基礎(chǔ)服務(wù)。大多數(shù)服務(wù)屬于邏輯服務(wù),接入服務(wù)如登錄、發(fā)消息、支付服務(wù),每日請(qǐng)求量在 10 億-100 億之間,入口協(xié)議觸發(fā)對(duì)邏輯服務(wù)和基礎(chǔ)服務(wù)更多的請(qǐng)求,核心服務(wù)每秒要處理上億次的請(qǐng)求。 在大規(guī)模微服務(wù)場(chǎng)景下,過(guò)載會(huì)變得比較復(fù)雜。如果是單體服務(wù),一個(gè)事件只用一個(gè)請(qǐng)求。但微服務(wù)下,一個(gè)事件可能要請(qǐng)求很多的服務(wù),任何一個(gè)服務(wù)過(guò)載失敗,就會(huì)造成其他的請(qǐng)求都是無(wú)效的。如下圖所示: 比如在一個(gè)轉(zhuǎn)賬服務(wù)下,需要查詢分別兩者的卡號(hào),再查詢 A 時(shí)成功了,但查詢B失敗,對(duì)于查卡號(hào)這個(gè)事件就算失敗了,比如查詢成功率只有 50%,那對(duì)于查詢兩者卡號(hào)這個(gè)成功率只有 50% * 50% = 25% 了,一個(gè)事件調(diào)用的服務(wù)次數(shù)越多,那成功率就會(huì)越低。 如何判斷過(guò)載通常判斷過(guò)載可以使用吞吐量、延遲、CPU 使用率、丟包率、待處理請(qǐng)求數(shù)、請(qǐng)求處理事件等等。微信使用在請(qǐng)求在隊(duì)列中的平均等待時(shí)間作為判斷標(biāo)準(zhǔn),就是從請(qǐng)求到達(dá),到開始處理的時(shí)間。 為啥不使用響應(yīng)時(shí)間?因?yàn)轫憫?yīng)時(shí)間是跟服務(wù)相關(guān)的,很多微服務(wù)是鏈?zhǔn)秸{(diào)用,響應(yīng)時(shí)間是不可控的,也是無(wú)法標(biāo)準(zhǔn)化的,很難作為一個(gè)統(tǒng)一的判斷依據(jù)。 那為什么不使用 CPU 負(fù)載作為判斷標(biāo)準(zhǔn)呢?因?yàn)?CPU 負(fù)載高不代表服務(wù)過(guò)載,一個(gè)服務(wù)請(qǐng)求處理及時(shí),CPU 處于高位反而是比較良好的表現(xiàn)。實(shí)際上 CPU 負(fù)載高,監(jiān)控服務(wù)是會(huì)告警出來(lái),但是并不會(huì)直接進(jìn)入過(guò)載處理流程。 騰訊微服務(wù)默認(rèn)的超時(shí)時(shí)間是 500ms,通過(guò)計(jì)算每秒或每 2000 個(gè)請(qǐng)求的平均等待時(shí)間是否超過(guò) 20ms,判斷是否過(guò)載,這個(gè) 20ms 是根據(jù)微信后臺(tái) 5 年摸索出來(lái)的門檻值。 采用平均等待時(shí)間還有一個(gè)好處是這個(gè)是獨(dú)立于服務(wù)的,可以應(yīng)用于任何場(chǎng)景,而不用關(guān)聯(lián)于業(yè)務(wù),可以直接在框架上進(jìn)行改造。 當(dāng)平均等待時(shí)間大于 20ms 時(shí),以一定的降速因子過(guò)濾調(diào)部分請(qǐng)求。如果判斷平均等待時(shí)間小于 20ms,則以一定的速率提升通過(guò)率。一般采用快降慢升的策略,防止大的服務(wù)波動(dòng)。整個(gè)策略相當(dāng)于一個(gè)負(fù)反饋電路。 過(guò)載保護(hù)策略一旦檢測(cè)到服務(wù)過(guò)載,需要按照一定的策略對(duì)請(qǐng)求進(jìn)行過(guò)濾。前面分析過(guò),對(duì)于鏈?zhǔn)秸{(diào)用的微服務(wù)場(chǎng)景,隨機(jī)丟棄請(qǐng)求會(huì)導(dǎo)致整體服務(wù)的成功率很低。所以請(qǐng)求是按照優(yōu)先級(jí)進(jìn)行控制的, 優(yōu)先級(jí)低的請(qǐng)求會(huì)優(yōu)先丟棄。 1)業(yè)務(wù)優(yōu)先級(jí) 對(duì)于不同的業(yè)務(wù)場(chǎng)景優(yōu)先級(jí)是不同的。比如登錄場(chǎng)景是最重要的業(yè)務(wù),不能登錄一切都白費(fèi)。支付消息也比普通消息優(yōu)先級(jí)高,因?yàn)橛脩魧?duì)金錢是更敏感的。但普通消息又比朋友圈消息優(yōu)先級(jí)高。所以在微信內(nèi)是天然存在業(yè)務(wù)優(yōu)先級(jí)的。 用戶的每個(gè)請(qǐng)求都會(huì)分配一個(gè)優(yōu)先級(jí)。在微服務(wù)的鏈?zhǔn)秸{(diào)用下,下游請(qǐng)求的優(yōu)先級(jí)也是繼承的。比如我請(qǐng)求登錄,那么檢查賬號(hào)密碼等一系列的的后續(xù)請(qǐng)求都是繼承登錄優(yōu)先級(jí)的,這就保證了優(yōu)先級(jí)的一致性。 每個(gè)后臺(tái)服務(wù)維護(hù)了業(yè)務(wù)優(yōu)先級(jí)的hash表。微信的業(yè)務(wù)太多,并非每個(gè)業(yè)務(wù)都記錄在表里,不在表里的業(yè)務(wù)就是最低優(yōu)先級(jí)。 2)用戶優(yōu)先級(jí)很明顯,只基于業(yè)務(wù)優(yōu)先級(jí)的控制是不夠的。首先不可能因?yàn)樨?fù)載高,丟棄或允許通過(guò)一整個(gè)業(yè)務(wù)的請(qǐng)求。每個(gè)業(yè)務(wù)的請(qǐng)求量很大,那一定會(huì)造成負(fù)載的大幅波動(dòng)。另外如果在業(yè)務(wù)中隨機(jī)丟棄請(qǐng)求,在過(guò)載情況下還是會(huì)導(dǎo)致整體成功率很低。 解決這個(gè)問題可以引入用戶優(yōu)先級(jí)。首先用戶優(yōu)先級(jí)也不應(yīng)該相同,對(duì)于普通人來(lái)說(shuō)通過(guò) hash 用戶唯一 ID,計(jì)算用戶優(yōu)先級(jí),為了防止出現(xiàn)總是打豆豆的現(xiàn)象,hash 函數(shù)每小時(shí)更換,跟業(yè)務(wù)優(yōu)先級(jí)一樣,單個(gè)用戶的訪問鏈條上的優(yōu)先級(jí)總是一致的。 為啥不采用會(huì)話 ID 計(jì)算優(yōu)先級(jí)呢?從理論上來(lái)說(shuō)采用會(huì)話 ID 和用戶 ID 效果是一樣的。但是采用會(huì)話 ID 在用戶重新登錄時(shí)刷新,這個(gè)時(shí)候可能用戶的優(yōu)先級(jí)可能變了,在過(guò)載的情況下,可能因?yàn)樘岣吡藘?yōu)先級(jí)就恢復(fù)了。這樣用戶會(huì)養(yǎng)成壞習(xí)慣,在服務(wù)有問題時(shí)就會(huì)重新登錄,這樣無(wú)疑進(jìn)一步加劇了服務(wù)的過(guò)載情況。 引入了用戶優(yōu)先級(jí),那就和業(yè)務(wù)優(yōu)先級(jí)組成了一個(gè)二維控制平面。根據(jù)負(fù)載情況,決定這臺(tái)服務(wù)器的準(zhǔn)入優(yōu)先級(jí)(B,U),當(dāng)過(guò)來(lái)的請(qǐng)求業(yè)務(wù)優(yōu)先級(jí)大于 B,或者業(yè)務(wù)優(yōu)先級(jí)等于 B,但用戶優(yōu)先級(jí)高于 U 時(shí),則通過(guò),否則決絕。 3)自適應(yīng)優(yōu)先級(jí)調(diào)整在大規(guī)模微服務(wù)場(chǎng)景下,服務(wù)器的負(fù)載是變化非常頻繁的,所以服務(wù)器的準(zhǔn)入優(yōu)先級(jí)是需要?jiǎng)討B(tài)變化的。微信分了幾十個(gè)業(yè)務(wù)優(yōu)先級(jí),每個(gè)業(yè)務(wù)優(yōu)先級(jí)下有 128 個(gè)用戶優(yōu)先級(jí),所以總的優(yōu)先級(jí)是幾千個(gè)。 如何根據(jù)負(fù)載情況調(diào)整優(yōu)先級(jí)呢?最**簡(jiǎn)單的方式是從右到左遍歷,每調(diào)整一次判斷下負(fù)載情況,這個(gè)時(shí)間復(fù)雜度是 O(n), 就算使用二分法,時(shí)間復(fù)雜度也為 O(logn)。**在數(shù)千個(gè)優(yōu)先級(jí)下,可能需要數(shù)十次調(diào)整才能確定一個(gè)合適的優(yōu)先級(jí),每次調(diào)整好再統(tǒng)計(jì)優(yōu)先級(jí),可能幾十秒都過(guò)去了,這個(gè)方法無(wú)疑是非常低效的。 微信提出了一種基于直方圖統(tǒng)計(jì)的方法快速調(diào)整準(zhǔn)入優(yōu)先級(jí),服務(wù)器上維護(hù)者目前準(zhǔn)入優(yōu)先級(jí)下,過(guò)去一個(gè)周期的(1s 或 2000 次請(qǐng)求)每個(gè)優(yōu)先級(jí)的請(qǐng)求量,當(dāng)過(guò)載時(shí),通過(guò)消減下一個(gè)周期的請(qǐng)求量來(lái)減輕負(fù)載,假設(shè)上一個(gè)周期所有優(yōu)先級(jí)的通過(guò)的請(qǐng)求總和是N。下一個(gè)周期的請(qǐng)求量要減少N*a,怎么去減少呢?每提升一個(gè)優(yōu)先級(jí)就減少一定的請(qǐng)求量,一直提升到減少的數(shù)目大于目標(biāo)量,恢復(fù)負(fù)載使用相反的方法,只不是系數(shù)為b,比a小,也是為了快降慢升。根據(jù)經(jīng)驗(yàn)值a為 5%,b為1%。 為了進(jìn)一步減輕過(guò)載機(jī)器的壓力,能不能在下游過(guò)載的情況下不把請(qǐng)求發(fā)到下游呢?否則下游還是要接受請(qǐng)求、解包、丟棄請(qǐng)求,白白浪費(fèi)帶寬也加重了下游的負(fù)載。 為了實(shí)現(xiàn)這個(gè)能力,在每次請(qǐng)求下游服務(wù)時(shí),下游把當(dāng)前服務(wù)的準(zhǔn)入優(yōu)先級(jí)返回給上游,上游維護(hù)下游服務(wù)的準(zhǔn)入優(yōu)先級(jí),如果發(fā)現(xiàn)請(qǐng)求優(yōu)先級(jí)達(dá)不到下游服務(wù)的準(zhǔn)入門檻,直接丟棄,而不再請(qǐng)求下游,進(jìn)一步減輕下游的壓力。 總結(jié)微信整個(gè)負(fù)載控制的流程如圖所示: 當(dāng)用戶從微信發(fā)起請(qǐng)求,請(qǐng)求被路由到接入層服務(wù),分配統(tǒng)一的業(yè)務(wù)和用戶優(yōu)先級(jí),所有到下游的字請(qǐng)求都繼承相同的優(yōu)先級(jí)。根據(jù)業(yè)務(wù)邏輯調(diào)用1個(gè)或多個(gè)下游服務(wù)。當(dāng)服務(wù)收到請(qǐng)求,首先根據(jù)自身服務(wù)準(zhǔn)入優(yōu)先級(jí)判斷請(qǐng)求是接受還是丟棄。服務(wù)本身根據(jù)負(fù)載情況周期性的調(diào)整準(zhǔn)入優(yōu)先級(jí)。當(dāng)服務(wù)需要再向下游發(fā)起請(qǐng)求時(shí),判斷本地記錄的下游服務(wù)準(zhǔn)入優(yōu)先級(jí)。如果小于則丟棄,如果沒有記錄或優(yōu)先級(jí)大于記錄則向下游發(fā)起請(qǐng)求。下游服務(wù)返回上游服務(wù)需要的信息,并且在信息中攜帶自身準(zhǔn)入優(yōu)先級(jí)。上游接受到返回后解析信息,并更新本地記錄的下游服務(wù)準(zhǔn)入優(yōu)先級(jí)。 整個(gè)過(guò)載保護(hù)的策略有以下三個(gè)特點(diǎn):第一,業(yè)務(wù)無(wú)關(guān)的,使用請(qǐng)求等待時(shí)間而不是響應(yīng)時(shí)間來(lái)制定用戶和業(yè)務(wù)優(yōu)先級(jí),這些都與業(yè)務(wù)本身無(wú)關(guān)。第二,獨(dú)立控制和聯(lián)合控制結(jié)合,準(zhǔn)入優(yōu)先級(jí)取決于獨(dú)立的服務(wù),但又可以聯(lián)合下游服務(wù)的情況,優(yōu)化服務(wù)過(guò)載時(shí)的表現(xiàn)。第三,高效且公平。請(qǐng)求鏈條的優(yōu)先級(jí)是一致的,并且會(huì)定時(shí)改變hash函數(shù)調(diào)整用戶優(yōu)先級(jí)。過(guò)載情況下,不會(huì)總是影響固定的用戶。 該文章在 2023/6/25 9:06:20 編輯過(guò) |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |