深入理解云原生下自適應(yīng)限流技術(shù)原理與應(yīng)用
作者簡(jiǎn)介:?jiǎn)套吭?,?9年畢業(yè),熱愛開源,樂于思考。擁有基礎(chǔ)架構(gòu)和游戲領(lǐng)域的一線開發(fā)經(jīng)驗(yàn)。獨(dú)立負(fù)責(zé)過大規(guī)模后端服務(wù)的開發(fā)與性能測(cè)試平臺(tái)搭建。
前言
本文將深入討論現(xiàn)今后端服務(wù)關(guān)于負(fù)載的測(cè)量、優(yōu)化、治理手段,通過對(duì)比分析TCP-BBR技術(shù)核心痛點(diǎn),進(jìn)而帶讀者盡可能理解并掌握自適應(yīng)限流技術(shù)。
背景
負(fù)載(load),通常與并發(fā)關(guān)系密切。對(duì)于后端服務(wù)而言,任意時(shí)間內(nèi)的并發(fā)用戶訪問都會(huì)提升服務(wù)負(fù)載,進(jìn)而進(jìn)一步消耗計(jì)算資源。然而計(jì)算資源是有限的,如CPU、memory、network等等,過載將會(huì)導(dǎo)致服務(wù)性能下降,進(jìn)而回復(fù)滯緩甚至不可用。描述服務(wù)應(yīng)對(duì)日益增長(zhǎng)的負(fù)載的能力稱之為Scalability, 即可伸縮/擴(kuò)展性。以下是DDIA(Designing Data Intensive Application)中的解釋:
Scalability is the term we use to describe a system’s ability to cope with increased load
測(cè)量負(fù)載
負(fù)載的具體指標(biāo)
為了提升可擴(kuò)展性,我們第一步要做的是固定資源下對(duì)服務(wù)請(qǐng)求接口進(jìn)行測(cè)試,進(jìn)行服務(wù)能力上限評(píng)估以及相應(yīng)優(yōu)化。顯然,單純將并發(fā)用于對(duì)負(fù)載的測(cè)量是不夠具體的。對(duì)于后端服務(wù),我們會(huì)進(jìn)一步關(guān)注以下兩個(gè)指標(biāo)。
負(fù)載因子(load parameter)

本服務(wù)中最能引起現(xiàn)負(fù)載增加的指標(biāo)。對(duì)于web服務(wù)而言,顯然就是每秒請(qǐng)求數(shù)QPS;對(duì)于數(shù)據(jù)庫等服務(wù)來說,可能就是讀寫率的上升。
關(guān)鍵結(jié)果(performance number)

本服務(wù)負(fù)載增加時(shí)最需要關(guān)注的指標(biāo)。對(duì)于web服務(wù)而言,顯然就是用戶的等待時(shí)間即respond time/latency;對(duì)于數(shù)據(jù)庫等服務(wù)來說,就是對(duì)吞吐率的影響。
性能觀測(cè)
以web服務(wù)為例,我們通過增加請(qǐng)求數(shù)來收集對(duì)應(yīng)的時(shí)延消耗如下圖。其中X軸表示請(qǐng)求的數(shù)量,Y軸表示對(duì)應(yīng)消耗的時(shí)間。其中,每次請(qǐng)求所消耗的時(shí)間通過灰色柱如所示。從實(shí)踐經(jīng)歷來看,同類型的請(qǐng)求每次消耗的時(shí)延會(huì)受到多種因素的影響,因而都不是固定的。(后文會(huì)詳細(xì)解釋)

所以,單純將總請(qǐng)求數(shù)/總耗時(shí)的平均結(jié)果不具代表性。為此業(yè)內(nèi)廣泛使用百分位數(shù),又稱水位線來表示用戶實(shí)際經(jīng)歷的時(shí)延情況。
p?N?th
其中N表示百分比,例如圖中95th,又稱95線。假設(shè)95線對(duì)應(yīng)的時(shí)延為1.5s,總請(qǐng)求數(shù)為100, 那么p95就表示在100個(gè)請(qǐng)求中有95個(gè)請(qǐng)求的時(shí)延小于1.5s。
在大型后端服務(wù)中,工程師會(huì)更傾向于關(guān)注高水位線如99th下的時(shí)延分布,因?yàn)檫@是最直接對(duì)用戶產(chǎn)生影響的關(guān)鍵結(jié)果。
定位優(yōu)化
通過水位線獲得的時(shí)延分布可以針對(duì)性的優(yōu)化相應(yīng)接口邏輯,以盡可能減少業(yè)務(wù)層導(dǎo)致的延遲較大的問題。如優(yōu)化代碼數(shù)據(jù)結(jié)構(gòu)、緩存部署、數(shù)據(jù)隔離以提高命中率等等。
資源與性能瓶頸
請(qǐng)求時(shí)延的增高不僅與業(yè)務(wù)邏輯有關(guān),在有限的硬件資源下(cpu、memory、帶寬),負(fù)載升高同樣會(huì)進(jìn)一步影響用戶請(qǐng)求時(shí)延。在對(duì)預(yù)期正常流量下的請(qǐng)求表現(xiàn)出的時(shí)延進(jìn)行業(yè)務(wù)優(yōu)化后,我們還需要對(duì)服務(wù)進(jìn)一步壓測(cè)以至過載情況下服務(wù)對(duì)請(qǐng)求的響應(yīng)情況,盡可能找出負(fù)載上限,保護(hù)服務(wù)。
壓測(cè)視圖

當(dāng)我們使用壓測(cè)工具一步步加大強(qiáng)度時(shí),關(guān)于負(fù)載各項(xiàng)指標(biāo)的宏觀理想變化基本如上圖所示。其中紅色曲線表示每秒請(qǐng)求被處理的用戶數(shù)TPS(throughput per second),藍(lán)色曲線表示對(duì)應(yīng)請(qǐng)求時(shí)延,X軸表示QPS量。
可以直觀的看出:
初始階段
隨著QPS的上升,TPS也在逐步提高,時(shí)延相對(duì)穩(wěn)定。因?yàn)榇藭r(shí)CPU和內(nèi)存等計(jì)算資源相對(duì)充裕,請(qǐng)求在系統(tǒng)中無需排隊(duì)就可以處理。
瓶頸階段
當(dāng)QPS加大到對(duì)應(yīng)TPS曲線頂點(diǎn)附近時(shí),此時(shí)計(jì)算資源負(fù)載接近滿額,服務(wù)中的請(qǐng)求處理出現(xiàn)排隊(duì)情況,時(shí)間非線性上漲。
過載階段
此時(shí)進(jìn)一步加壓QPS,計(jì)算資源超額負(fù)載,進(jìn)一步導(dǎo)致GC、進(jìn)程調(diào)度、網(wǎng)絡(luò)等資源壓力導(dǎo)致TPS迅速下降,同理請(qǐng)求排隊(duì)過長(zhǎng)導(dǎo)致時(shí)延進(jìn)一步類指數(shù)級(jí)上升,最終導(dǎo)致服務(wù)資源耗盡出現(xiàn)宕機(jī)無響應(yīng)等情況。
直觀來看,由于固定計(jì)算資源的限制,當(dāng)QPS加壓超過TPS峰值時(shí)系統(tǒng)的處理能力會(huì)被反噬。從服務(wù)保護(hù)、資源利用的角度,我們會(huì)認(rèn)為將TPS頂點(diǎn)附近值作為對(duì)QPS的限制閾值相對(duì)理想。
過載保護(hù)
本節(jié)將著重介紹基于具體閾值的限流辦法以及相關(guān)的局限性,提出服務(wù)穩(wěn)定前提下的核心預(yù)期。
基于QPS閾值的限流
在很長(zhǎng)一段時(shí)間內(nèi),工程師都會(huì)采用上述閾值作為服務(wù)的兜底QPS限制以防止過載。此時(shí)就出現(xiàn)了優(yōu)秀的限流算法,即令牌桶(Token-bucket)和漏桶(leak-bucket)以及眾多衍生。

互聯(lián)網(wǎng)上關(guān)于這兩種算法原理的介紹多如牛毛,在這就不再贅述??偟膩碚f,桶算法對(duì)流量提供了整形的功能,以防止瞬時(shí)流量等極端情況打垮服務(wù)。在小規(guī)模后端服務(wù)而言基于閾值的限流方式簡(jiǎn)單高效,能夠應(yīng)付絕大多數(shù)問題。
基于具體閾值的局限性
上節(jié)在進(jìn)行閾值測(cè)量時(shí),對(duì)測(cè)試的整體描述為理想情況下。這意味著在大規(guī)模分布式系統(tǒng)中,測(cè)量結(jié)果從工程的角度忽略了很多必要細(xì)節(jié)。這其中包括測(cè)試方式、硬件資源配額、線上scale、服務(wù)環(huán)境、閾值可用性等等眾多情況。
測(cè)試之痛
現(xiàn)如今后端領(lǐng)域已經(jīng)進(jìn)入了云原生時(shí)代,微服務(wù)架構(gòu)早已經(jīng)是事實(shí)標(biāo)準(zhǔn)。其中通訊方面是以典型的事件驅(qū)動(dòng)方式(Event-driven),這表示所有的請(qǐng)求都是通過RPC即以接口調(diào)用的方式實(shí)現(xiàn)的。一個(gè)合理劃分的微服務(wù),在稍具規(guī)模的情況下?lián)碛械慕涌跀?shù)量保守估計(jì)至少是在十位數(shù)以上,因此對(duì)接口級(jí)別、服務(wù)級(jí)別的閾值獲取將變得相當(dāng)繁瑣和困難。在Netflix的技術(shù)博客中,他們使用arduous task來形容測(cè)試的艱巨。
接口壓測(cè)
如前文提到的,即使是同一個(gè)接口,每輪壓測(cè)的結(jié)果也會(huì)有差距。這是因?yàn)闄C(jī)器資源在測(cè)試中同時(shí)會(huì)受到垃圾回收、線程/進(jìn)程資源搶占、磁盤I/O抖動(dòng)、網(wǎng)絡(luò)帶寬質(zhì)量等因素的影響。在服務(wù)級(jí)別的測(cè)試、線上環(huán)境中,此類問題會(huì)被進(jìn)一步放大。為此,測(cè)試人員不得不多次測(cè)量求個(gè)均值以求準(zhǔn)確。
服務(wù)級(jí)壓測(cè)
不同接口顯然對(duì)資源的消耗程度不同,因其包含讀、寫、下游扇出等各種情況時(shí)延也會(huì)產(chǎn)生差異。對(duì)于服務(wù)級(jí)別的限流閾值確定可以說是相當(dāng)困難,因?yàn)榛诮涌诩?jí)的閾值組合會(huì)在不同用戶請(qǐng)求不同接口時(shí)產(chǎn)生波動(dòng)。為此進(jìn)行流量錄制并全鏈路壓測(cè)雖能夠閾值的范圍,但仍無法做到精準(zhǔn),且成本過大。
維護(hù)之痛
所有測(cè)試以及線上環(huán)境中,最關(guān)鍵其實(shí)是計(jì)算資源的配置。所測(cè)試機(jī)器的CPU核心數(shù)和內(nèi)存等配置時(shí)刻影響著測(cè)試結(jié)果。再者,如今基于容器網(wǎng)絡(luò)如k8s的自動(dòng)資源scale能力,會(huì)使得基于具體數(shù)字的閾值設(shè)定難以維護(hù)。再者,秒級(jí)的流量限制顯然對(duì)于100ms內(nèi)突發(fā)流量洪峰無法做出有效干預(yù),導(dǎo)致服務(wù)異常過載。
干預(yù)成本
當(dāng)無論遇到線上激增流量機(jī)器指標(biāo)異常、自動(dòng)擴(kuò)容等導(dǎo)致閾值不再適用,進(jìn)而要增加或者減小時(shí),工程師從發(fā)現(xiàn)問題到上機(jī)操作發(fā)布配置的耗時(shí)已經(jīng)錯(cuò)過了干預(yù)的最佳時(shí)機(jī)。
認(rèn)知負(fù)擔(dān)
從上述測(cè)量流程來看,工程師必須對(duì)計(jì)算資源、接口業(yè)務(wù)、容器配合、調(diào)度時(shí)機(jī)等各方面有足夠認(rèn)知才能應(yīng)對(duì)各種流量突發(fā)狀況,顯然會(huì)消耗大量的精力和時(shí)間。
綜上所述,基于具體數(shù)字的閾值方式在大規(guī)模后端服務(wù)中會(huì)進(jìn)一步捉襟見肘,可延展和可伸縮性較差。
過載下的服務(wù)預(yù)期
在討論進(jìn)一步的解決方案之前,我們要明確在負(fù)載加大時(shí)對(duì)服務(wù)情況的預(yù)期。即在使服務(wù)不垮掉情況下,充分利用計(jì)算資源服務(wù)用戶。結(jié)合具體的負(fù)載因素預(yù)期如下:
盡可能多的處理request,即高TPS 盡可能快的處理request,即低時(shí)延 盡可能使得request不產(chǎn)生堆積,當(dāng)前計(jì)算資源下存在的最佳處理量

很明顯,想要同時(shí)獲取上述指標(biāo)是相悖的。對(duì)于高TPS,意味著盡可能要將計(jì)算資源利用,請(qǐng)求產(chǎn)生擁塞進(jìn)而導(dǎo)致關(guān)鍵結(jié)果——時(shí)延的指數(shù)上升;對(duì)于時(shí)延,那么就盡可能需要服務(wù)中處理的請(qǐng)求相對(duì)寬松。雖然時(shí)延低下來了但同時(shí)TPS也會(huì)很低,造成計(jì)算資源的浪費(fèi);對(duì)于服務(wù)進(jìn)程中的請(qǐng)求最佳處理量,結(jié)合各種變化因素也不好確定。那么有最佳的解決辦法嗎?
傳輸層自適應(yīng)擁塞控制
本節(jié)將引出傳輸層TCP-BBR算法要解決的問題,以及和我們上述服務(wù)預(yù)期問題的同質(zhì)性。進(jìn)一步挖出共通點(diǎn)。
傳輸層TCP現(xiàn)狀
TCP是面向字節(jié)流保序的傳輸層協(xié)議。在面對(duì)未知的網(wǎng)絡(luò)中轉(zhuǎn)節(jié)點(diǎn)以及對(duì)端網(wǎng)絡(luò)狀態(tài),如果最大化利用網(wǎng)絡(luò)帶寬是其要面對(duì)的首要問題之一。為此,應(yīng)運(yùn)而生了經(jīng)典的擁塞控制算法。

上圖是傳統(tǒng)的基于丟包檢測(cè)的擁塞控制算法,代表有CUBIC、RENO等實(shí)現(xiàn)。詳細(xì)解析已經(jīng)超出本文的討論范圍,此處簡(jiǎn)述基本原理以及其核心特點(diǎn)。其控制周期基本上有四個(gè)組成部分,分別是慢啟動(dòng)、擁塞避免、快重傳、快恢復(fù)。其中CWND表示擁塞窗口,也就是可以向網(wǎng)絡(luò)中一次性發(fā)送的數(shù)據(jù)量,最終窗口大小由其與對(duì)端接收窗口中的較小值來決定。
在不考慮對(duì)端窗口的影響下,由于網(wǎng)絡(luò)狀況未知,算法會(huì)依據(jù)不同狀態(tài)針對(duì)性的加大CWND窗口的大小。當(dāng)出現(xiàn)丟包時(shí)CWND大小會(huì)大幅度減小,如圖中陡降部分所示。在后續(xù)的優(yōu)化重傳等策略之下,仍然能夠看到當(dāng)出現(xiàn)丟包時(shí)CWND的減幅程度十分明顯。
基于丟包的控制算法局限

該類算法的核心思想在于對(duì)丟包數(shù)據(jù)的測(cè)量,然而在現(xiàn)代網(wǎng)絡(luò)設(shè)施的發(fā)展下某些場(chǎng)景如長(zhǎng)肥網(wǎng)絡(luò)(高延遲、高帶寬)等已經(jīng)不再適用。結(jié)合上圖,由于路由節(jié)點(diǎn)的buffer大小日益增長(zhǎng),同時(shí)增加了數(shù)據(jù)包在隊(duì)列中等待時(shí)間。再加上多跳路由節(jié)點(diǎn)的計(jì)算、傳輸?shù)饶芰Φ牟煌瑫?huì)很容易導(dǎo)致CUBIC等算法的認(rèn)為丟包,采取銳減CWND窗口的行為。也就說當(dāng)開始介入擁塞避免時(shí),擁塞早已發(fā)生且會(huì)進(jìn)一步負(fù)反饋導(dǎo)致吞吐下降。也就是說,基于檢測(cè)丟包信號(hào)這單一指標(biāo)來衡量擁塞達(dá)不到最優(yōu)解。
基于真實(shí)的網(wǎng)絡(luò)負(fù)載特點(diǎn)分析
顯然網(wǎng)絡(luò)擁塞的發(fā)生同樣是由各個(gè)路由節(jié)點(diǎn)網(wǎng)絡(luò)負(fù)載升高產(chǎn)生的,既然是負(fù)載,那么就需要對(duì)其具體負(fù)載指標(biāo)的分析。
負(fù)載因子
拋開網(wǎng)絡(luò)拓?fù)洮F(xiàn)狀的復(fù)雜性,能夠首要引起網(wǎng)絡(luò)負(fù)載升高的顯然是網(wǎng)絡(luò)流量的增加。那么,對(duì)應(yīng)的指標(biāo)就是網(wǎng)絡(luò)Bps(bytes per second),即每秒的數(shù)據(jù)傳輸量。
關(guān)鍵結(jié)果
面對(duì)負(fù)載Bps的增加,會(huì)影響到的關(guān)鍵結(jié)果就是網(wǎng)絡(luò)傳輸?shù)耐禃r(shí)延RTT(Round-trip time)上升,導(dǎo)致傳輸擁塞。
傳輸觀測(cè)
下圖是理想情況下網(wǎng)絡(luò)設(shè)備在傳輸過程中的負(fù)載變化圖,上半部分為流量傳輸速度(Delivery rate≈Bps)和對(duì)應(yīng)時(shí)延(RTT)的變化,下半部分為對(duì)應(yīng)網(wǎng)絡(luò)設(shè)備的物理狀態(tài)變化。分為三個(gè)階段:

初始階段
初始階段網(wǎng)絡(luò)設(shè)備中的隊(duì)列為空,數(shù)據(jù)包無需排隊(duì)。隨著發(fā)送速率的增加RTT保持穩(wěn)定。
瓶頸階段
此時(shí)排隊(duì)已經(jīng)逐漸形成,設(shè)備的傳輸吞吐能力即帶寬達(dá)到上限,保持穩(wěn)定。對(duì)于RTT而言,由于排隊(duì)的原因逐漸增加。
擁塞階段
此時(shí)網(wǎng)絡(luò)設(shè)備的隊(duì)列已經(jīng)被流量完全打滿,多余的數(shù)據(jù)包被拒絕,進(jìn)一步導(dǎo)致RTT大幅上升。
從圖中紅圈標(biāo)注的RTT和傳輸率折線圖出看到,RTT在瓶頸階段開始大幅度上升,傳輸速率在該階段同樣達(dá)到了瓶頸即帶寬上限。可以看出,從此處開始擁塞就要發(fā)生了,然而基于丟包的擁塞控制算法反而在上述第三階段才能夠生效,這顯然滯后太多了。
最佳預(yù)期
從上述理想分析,我們相對(duì)確定出了在一波數(shù)據(jù)傳輸過程中RTT的下限,即minRTT;數(shù)據(jù)傳輸帶寬能達(dá)到的上限 max BW(max bandwidth)。對(duì)于這兩個(gè)網(wǎng)絡(luò)層的負(fù)載指標(biāo),至今已經(jīng)有了很成熟的衡量體系,在網(wǎng)絡(luò)傳輸中其關(guān)系如下圖所示。

我們可通過?網(wǎng)絡(luò)節(jié)點(diǎn)每秒傳輸數(shù)據(jù)的能力即帶寬(bandwidth)乘?數(shù)據(jù)包在節(jié)點(diǎn)中的傳輸處理時(shí)間時(shí)延(即latency≈RTT)的結(jié)果作為網(wǎng)絡(luò)上正在處理的報(bào)文數(shù)量,即帶寬時(shí)延積BDP。BDP決定了需要向網(wǎng)絡(luò)中發(fā)送的數(shù)據(jù)包量。
綜合上述,我們可以確定理想情況下向網(wǎng)絡(luò)傳輸數(shù)據(jù)的最佳數(shù)量,即瓶頸期指標(biāo)特征:?BestBDP = maxBW * minRTT
顯然,我們應(yīng)對(duì)負(fù)載的關(guān)鍵指標(biāo)測(cè)量已經(jīng)不僅要關(guān)注關(guān)鍵結(jié)果,還與負(fù)載因子進(jìn)行綜合。相對(duì)的,多年來基于丟包的擁塞控制算法都只關(guān)注了時(shí)延這單一指標(biāo)。
雖然有了理論預(yù)期,但同樣出現(xiàn)擺在工程師面前的難題:如何在拓?fù)浣Y(jié)構(gòu)復(fù)雜,路由情況多變的網(wǎng)絡(luò)環(huán)境中測(cè)得這兩個(gè)指標(biāo)??這個(gè)測(cè)試過程本質(zhì)上和上節(jié)我們提出的業(yè)務(wù)負(fù)載預(yù)期指標(biāo)相悖論如出一轍。那么該如何解決這一難題呢?接下來就是BBR登場(chǎng)的時(shí)刻。
TCP-BBR算法
BBR(Bottleneck Bandwidth and Round-trip propagation time)是Google近年來提出的擁塞控制算法,誕生后大幅度提高了在高延遲等情況下網(wǎng)絡(luò)傳輸?shù)耐掏?。從命名就可以看到帶寬(Bandwidth)和往返時(shí)延(Round-trip time)關(guān)鍵字,在上述鋪墊過程中,對(duì)應(yīng)的就是maxBW和minRTT。對(duì)于多變的網(wǎng)絡(luò)環(huán)境BBR大膽的采用了以預(yù)期公式驅(qū)動(dòng),實(shí)時(shí)交替探測(cè)兩個(gè)負(fù)載指標(biāo)的辦法,下文會(huì)對(duì)此詳細(xì)解釋。
算法核心解讀
BBR認(rèn)為:既然網(wǎng)絡(luò)多變,且最佳帶寬和時(shí)延不好同時(shí)測(cè)量,那么就采取實(shí)時(shí)交替探測(cè)的方式。通過滑動(dòng)窗口細(xì)粒度的交替收集一段時(shí)間內(nèi)的每秒最大傳輸量和最小的RTT,通過計(jì)算就可以獲得目前最佳的BDP。即?BestBDP = BtlBw (bottleneck bandwidth) * RTprop (round-trip propagation time)
BtlBw
表示目前網(wǎng)絡(luò)中的瓶頸帶寬,也就是上節(jié)中的maxBW,是網(wǎng)絡(luò)設(shè)施傳輸?shù)纳舷?。BBR會(huì)取得一段時(shí)間內(nèi)滑動(dòng)窗口的統(tǒng)計(jì)的最大BtlBw值作為參考。其測(cè)量方式簡(jiǎn)述為一段時(shí)間內(nèi)的數(shù)據(jù)包總量除以他們所抵達(dá)花費(fèi)的時(shí)間。
RTprop
表示拋開任何外在噪音,如ack重發(fā)耗時(shí),網(wǎng)絡(luò)抖動(dòng)等等導(dǎo)致RTT偏高情況。即在滑動(dòng)窗口統(tǒng)計(jì)中的RTT最小值作為參考,其測(cè)量方式為數(shù)據(jù)包發(fā)送和回復(fù)耗時(shí)。
inflight
這指的是,在BBR工作期間內(nèi),已經(jīng)發(fā)送至網(wǎng)絡(luò)但是還是沒有收到答復(fù)的數(shù)據(jù)包。也就說真實(shí)的,在網(wǎng)絡(luò)設(shè)備里正在傳輸?shù)臄?shù)據(jù)量,即負(fù)載。有了預(yù)期公式計(jì)算出的BestBDP指導(dǎo)將RTprop與BtlBW相乘,BBR就可以得出當(dāng)前時(shí)刻外界網(wǎng)絡(luò)最佳的負(fù)載量與實(shí)際inflight的關(guān)系。有了這樣的簡(jiǎn)單的數(shù)值比對(duì),算法就可以控制發(fā)包的最佳量以進(jìn)行擁塞控制。
以下偽代碼直觀的體現(xiàn)了BBR算法在發(fā)包和收包時(shí)的處理邏輯。
收包 onAck

發(fā)包 send

算法組成
了解了核心的算法邏輯,接下來將簡(jiǎn)述其運(yùn)作周期,進(jìn)一步了解算法是如何充分利用未知網(wǎng)絡(luò)設(shè)備傳輸能力的。算法運(yùn)行狀態(tài)主要分為啟動(dòng)階段(Startup)、排空階段(Drain)、帶寬探測(cè)(ProbeBW)、時(shí)延探測(cè)(ProbeRTT)。同樣,本文將不會(huì)闡述具體細(xì)節(jié),具體細(xì)節(jié)可以參考文末的reference。我們回到核心關(guān)注點(diǎn),BBR是如何探測(cè)以及適應(yīng)當(dāng)前網(wǎng)絡(luò)設(shè)備傳輸能力的。

上圖摘自google BBR論文,展示了在穩(wěn)定網(wǎng)絡(luò)傳輸節(jié)點(diǎn)下BBR算法中關(guān)鍵指標(biāo)RTT、BW、inflight的變化圖。其中灰色cycle gain數(shù)組,相當(dāng)于滑動(dòng)窗口。其中每個(gè)元素裝載了帶寬探測(cè)時(shí)的增益系數(shù),通過與當(dāng)前最大BW相乘可以實(shí)現(xiàn)增加/減少向網(wǎng)絡(luò)中的數(shù)據(jù)發(fā)送,從而實(shí)現(xiàn)適應(yīng)未知網(wǎng)絡(luò)傳輸能力目的;同理,對(duì)于時(shí)延探測(cè),簡(jiǎn)單來說BBR同樣會(huì)周期性的發(fā)送小體量數(shù)據(jù)包收集最佳RTT。
可見,基于預(yù)期的負(fù)載控制算法,即同時(shí)集合負(fù)載因子和關(guān)鍵結(jié)果的計(jì)算,相比只關(guān)注一個(gè)指標(biāo)的實(shí)現(xiàn)方式在高吞吐場(chǎng)景下具備一定的優(yōu)越性。同時(shí),基于滑動(dòng)窗口細(xì)粒度的動(dòng)態(tài)探測(cè)極值,使得測(cè)量結(jié)果更具時(shí)效性與說服力。
自適應(yīng)限流算法
經(jīng)過上面長(zhǎng)篇大論的分析和討論,我們終于到了本章最為核心的自適應(yīng)限流算法介紹。目的在于找到應(yīng)用層與傳輸層面對(duì)負(fù)載、過載情況應(yīng)對(duì)措施的共同點(diǎn),以及如何應(yīng)用于后端服務(wù)。
負(fù)載在傳輸層的同質(zhì)性
綜合來看,傳輸層在面對(duì)網(wǎng)絡(luò)負(fù)載的測(cè)量、治理、實(shí)施歷程與應(yīng)用層負(fù)載有很多共同點(diǎn),如下表所示。
| 傳輸層 | 應(yīng)用層 | |
|---|---|---|
| 負(fù)載因子 | Bps | Qps |
| 關(guān)鍵結(jié)果 | RTT | latency |
| 加壓負(fù)載因子時(shí)處理端表現(xiàn) | delivery Rate(帶寬傳輸速率)上漲直至瓶頸 | TPS上漲直至瓶頸,由于進(jìn)程資源搶占等壓力會(huì)進(jìn)一步反噬下跌 |
| 加壓負(fù)載因子時(shí)關(guān)鍵結(jié)果表現(xiàn) | RTT從理想穩(wěn)定狀態(tài)升高至瓶頸,伴隨擁塞指數(shù)上升 | latency請(qǐng)求等待時(shí)延理想情況下至瓶頸期處于穩(wěn)定狀態(tài),伴隨排隊(duì)導(dǎo)致服務(wù)過載指數(shù)上升 |
| 核心預(yù)期 | 盡可能大利用網(wǎng)絡(luò)傳輸帶寬,即最大傳輸速率maxBW;最小的包等待RTT;不擁塞前提下最佳發(fā)包量BestBDP | 盡可能多的處理請(qǐng)求,即高TPS;盡可能快的處理請(qǐng)求,即低時(shí)延;極可能充分的利用服務(wù)計(jì)算資源,不產(chǎn)生堆積的數(shù)量 |
| 核心需求 | 能夠得到網(wǎng)絡(luò)設(shè)施的最佳處理數(shù)據(jù)量,以避免擁塞且實(shí)現(xiàn)高吞吐 | 能夠得到服務(wù)進(jìn)程在當(dāng)前計(jì)算資源下的最佳請(qǐng)求處理量,以避免請(qǐng)求排隊(duì)資源搶占實(shí)現(xiàn)高吞吐,進(jìn)一步保護(hù)服務(wù)防止過載 |
| 難點(diǎn) | 無法同時(shí)測(cè)量最佳帶寬和最低時(shí)延,基于復(fù)雜的網(wǎng)絡(luò)拓?fù)浜驮O(shè)施數(shù)量難以確定數(shù)值 | 無法同時(shí)測(cè)量最佳TPS和最佳latency,眾多接口耗時(shí)不同,且包含進(jìn)程GC、網(wǎng)絡(luò)抖動(dòng)、資源scale多變的情況下基于服務(wù)級(jí)別的限流數(shù)值不好確定 |
| 歷史階段 | 基于RTT耗時(shí)檢測(cè),即丟包認(rèn)為網(wǎng)絡(luò)發(fā)生擁塞進(jìn)行干預(yù) | 基于壓測(cè)得到的qps閾值作為限流標(biāo)準(zhǔn) |
| 現(xiàn)階段 | TCP-BBR | Sentinel、Kratos …… |
(我們會(huì)在后文介紹sentinel與kratos)
利特爾法則
通過上表可看出應(yīng)用層和傳輸層在應(yīng)對(duì)負(fù)載時(shí)本質(zhì)需求是相同的,那么關(guān)于應(yīng)用層的核心預(yù)期公式的推導(dǎo)顯然具有相似性,那就是最佳請(qǐng)求數(shù)TW(當(dāng)前最佳處理任數(shù)目) = TPS * latency。如下所示

其實(shí)這個(gè)公式的依據(jù)是顯赫有名的利特爾法則little's law,為通過對(duì)工業(yè)中平均生產(chǎn)數(shù)量和對(duì)應(yīng)耗時(shí)提供了理論基礎(chǔ),以進(jìn)一步衡量生產(chǎn)能力。
基于TCP-BBR的自適應(yīng)算法
我們已經(jīng)具備充分的理論基礎(chǔ)和傳輸層實(shí)現(xiàn)指導(dǎo),下一步就是因地制宜的實(shí)施在應(yīng)用層后臺(tái)服務(wù)。在業(yè)內(nèi)最初版目前所知是由阿里的sentinel組件引入,由kratos進(jìn)行了進(jìn)一步拓展。在此我們需要搞清楚兩個(gè)關(guān)鍵問題,才能保證最大化吞吐的同時(shí)防止服務(wù)過載。
控制時(shí)機(jī)
BBR作為數(shù)據(jù)發(fā)送方,需要面臨的問題未知網(wǎng)絡(luò)設(shè)施傳輸能力。由于網(wǎng)絡(luò)設(shè)施的傳輸能力、擁塞狀態(tài)對(duì)發(fā)送方是非直接可見的,所以才有了上文提到的BBR帶寬探測(cè)?;瑒?dòng)窗口內(nèi)通過cycle gain變化,來適應(yīng)不同時(shí)刻的傳輸能力。
應(yīng)用層作為請(qǐng)求處理方,無論是在容器網(wǎng)絡(luò)和物理機(jī)上部署,計(jì)算資源是相對(duì)固定的。這意味著存在著最佳處理量上限,我們要保證的是在流量上升或者因?yàn)槠渌蛩貙?dǎo)致計(jì)算資源緊張時(shí),通過計(jì)算出的最佳TW來限制入口流量。
控制信號(hào)
了解了控制時(shí)機(jī),可就是當(dāng)計(jì)算資源緊張時(shí)進(jìn)行干預(yù)。那么該如何確定資源緊張信號(hào)呢?總的來說就是CPU利用率或者操作系統(tǒng)負(fù)載,或者內(nèi)存、磁盤等資源。以入口流量特征來看(進(jìn)程RPC調(diào)用下游服務(wù)按照業(yè)務(wù)需求進(jìn)行組裝、計(jì)算、返回),無論時(shí)內(nèi)存資源不足導(dǎo)致的GC(依賴CPU)、磁盤I/O吞吐下降、調(diào)度搶占等等因素,都會(huì)導(dǎo)致用戶請(qǐng)求增加、序列化成本增加(CPU)進(jìn)而時(shí)延上升惡性循環(huán)。所以在sentinel和kratos的實(shí)現(xiàn)中都選擇了適用CPU作為資源信號(hào)限流,只不過前者使用的是cpu load1,后者使用的是服務(wù)基于cgroup對(duì)CPU的實(shí)時(shí)采樣使用率。
兩者使用各有千秋,但我們認(rèn)為,基于load1作為控制信號(hào)仍不夠敏感。在linux下操作系統(tǒng)load1表示一分鐘內(nèi)CPU的平均負(fù)載值,對(duì)于流量洪峰等過載的發(fā)生干預(yù)有效性較慢。
具體方案
本部分屏蔽到絕大部分代碼與設(shè)施細(xì)節(jié),關(guān)注應(yīng)用方式與過程中上線效果、遇到的問題以及優(yōu)化?;赽br算法的完整kratos代碼可進(jìn)入referenc閱讀
CPU利用率峰值信號(hào)
kratos以是當(dāng)前容器網(wǎng)絡(luò)服務(wù)CPU利用率的80%作為控制信號(hào)臨界點(diǎn),通過為此服務(wù)會(huì)開啟獨(dú)立的goroutine每隔250ms進(jìn)行基于本服務(wù)的cgroup(/sys/fs/cgroup/cpu/*)CPU占用信息采集,以及系統(tǒng)總cpu tick(proc/stat)占用采集。
對(duì)CPU占用率的計(jì)算本質(zhì)是間隔內(nèi)?本進(jìn)程占用的CPU時(shí)間增量/系統(tǒng)的總CPU時(shí)間占用增量。顯然CPU的變化是相當(dāng)迅速的,會(huì)受到各種因素的影響來回抖動(dòng)動(dòng)。為此我們采用了滑動(dòng)均值(算法原理參考)的辦法進(jìn)行降噪穩(wěn)定。通過確定參考衰退率β(<1),使得最終結(jié)果等于:β*上次的CPU占用率 + (1-β)*本次的時(shí)機(jī)測(cè)得CPU占用率。如下所示:

深藍(lán)色折線代表了正常實(shí)際測(cè)量下CPU的變化折線圖,可以看到抖動(dòng)十分不穩(wěn)定。 橘色公式折線表示了在滑動(dòng)均值算法下趨于平穩(wěn)變動(dòng)的CPU變化圖,但是能看到前提CPU數(shù)值較低。 綠色公式折線是在具體滑動(dòng)均值算法作用下,對(duì)前期數(shù)據(jù)量不足導(dǎo)致CPU起點(diǎn)低的問題進(jìn)行的偏差修正。
最終經(jīng)過上述修正,我們得到線上具有參考使用價(jià)值的CPU占用率。
Pass&RT
pass和RT分別表示處理完成請(qǐng)求數(shù)和對(duì)應(yīng)請(qǐng)求所消耗的時(shí)間,即TPS和Latency。相應(yīng)的,我們的測(cè)量辦法同樣是通過滑動(dòng)窗口對(duì)pass和RT進(jìn)行統(tǒng)計(jì),如下圖。

sample window表示窗口采樣周期,sample bucket表示周期內(nèi)的采樣批次。假設(shè)現(xiàn)在采樣窗口時(shí)間為1000ms,bucket采樣批次時(shí)間持續(xù)500ms,那么就表示在前500ms內(nèi)完成的請(qǐng)求數(shù)和這些請(qǐng)求消耗的平均時(shí)延都會(huì)被原子(atomic)統(tǒng)計(jì)在bucket1中。同理,當(dāng)?shù)?01ms會(huì)被統(tǒng)計(jì)在bucket2中,當(dāng)?shù)?001ms時(shí)會(huì)再次回到bucket1,以此類推??梢姰?dāng)bucket足夠多,以及統(tǒng)計(jì)間隔足夠小時(shí)最能夠得到真實(shí)的數(shù)據(jù),更有效的應(yīng)對(duì)秒內(nèi)流量洪峰。
流量干預(yù)
當(dāng)CPU利用率過載時(shí),就需要通過預(yù)期公式進(jìn)行干預(yù)了。我們會(huì)在服務(wù)運(yùn)行期間持續(xù)統(tǒng)計(jì)當(dāng)前服務(wù)的請(qǐng)求數(shù),即inflight,通過在滑動(dòng)窗口內(nèi)的所有buckets中比較得出最多請(qǐng)求完成數(shù)maxPass,以及最小的耗時(shí)minRT,相乘就得出了預(yù)期的最佳請(qǐng)求數(shù)maxFlight。通過inflight與maxFlight對(duì)比,如果前者大于后者那么就已經(jīng)過載,進(jìn)而拒絕后續(xù)到來的請(qǐng)求防止服務(wù)過載。
線上效果與調(diào)優(yōu)

上圖是在線上部署了基于kratos的自適應(yīng)算法后的效果圖,其中藍(lán)色曲線代表了并發(fā)訪問的用戶數(shù),深黃色代表對(duì)應(yīng)請(qǐng)求的時(shí)延,淺綠色則表示成功處理的請(qǐng)求數(shù)。左側(cè)為最終版,右側(cè)為第一版。
不難看出,第一版時(shí)當(dāng)算法控制后黃色的時(shí)延仍然很高,成功處理的請(qǐng)求數(shù)也并非穩(wěn)定。產(chǎn)生這樣結(jié)果的原因其實(shí)依然是CPU利用率很敏感且粒度很細(xì),當(dāng)CPU大于80%利用率閾值時(shí)算法生效,當(dāng)微量請(qǐng)求被拒絕時(shí)算法便停止了干預(yù)。最終的結(jié)果便是算法會(huì)因?yàn)榱髁康挠咳肱c拒絕中頻繁開啟與關(guān)閉,導(dǎo)致結(jié)果不符合預(yù)期。
優(yōu)化手段
為此我們簡(jiǎn)單加入了1s的冷卻時(shí)間,也就是說算法開啟后會(huì)持續(xù)至少1s的冷卻時(shí)間,再次期間保持算法開啟。當(dāng)冷卻時(shí)間過后會(huì)再次統(tǒng)計(jì)當(dāng)前CPU利用率,并根據(jù)閾值對(duì)比進(jìn)行持續(xù)或者關(guān)閉。最終測(cè)試結(jié)果如最終版左圖所示,在流量持續(xù)涌入的情況下請(qǐng)求的成功處理數(shù)和時(shí)延都十分穩(wěn)定。
后記
本文盡可能通過應(yīng)對(duì)負(fù)載的迭代歷程優(yōu)劣分析,抽絲剝繭完整的引出基于TCP-BBR的自適應(yīng)算法實(shí)現(xiàn)與優(yōu)勢(shì)。自適應(yīng)過載保護(hù)同樣不是完美的,一方面在請(qǐng)求等排隊(duì)過長(zhǎng)的情況下雖然計(jì)算資源未滿載,但是同樣增加了等待時(shí)間。為此,可以選擇結(jié)合排隊(duì)論相關(guān)算法如CoDel進(jìn)行干預(yù);另一方面,在極端流量的涌入下可能單純的拒絕回復(fù)成本就會(huì)打垮CPU,從而導(dǎo)致宕機(jī),此時(shí)可以參考reference中g(shù)oogle sre里的自適應(yīng)熔斷等策略合力保障服務(wù)的可用性。
另外,對(duì)于自適應(yīng)限流技術(shù)的發(fā)展,從Netflix最早基于傳輸層vegas的限流手段推出中間件concurrency-limits, 再到如今基于TCP-BBR的自適應(yīng)限流算法改進(jìn)與實(shí)現(xiàn),都體現(xiàn)了很強(qiáng)的發(fā)散性和優(yōu)化空間。期待讀者的進(jìn)一步討論!
Reference
Google SRE:https://sre.google/sre-book/handling-overload/
Alibaba Sentinel:https://sentinelguard.io/zh-cn/docs/system-adaptive-protection.html
Netflix 限流技術(shù):https://netflixtechblog.medium.com/performance-under-load-3e6fa9a60581
TensorFlow滑動(dòng)窗口簡(jiǎn)讀:https://blog.csdn.net/m0_38106113/article/details/81542863
Kratos 自適應(yīng)限流源碼:https://github.com/go-kratos/kratos/tree/v1.0.x/pkg/ratelimit/bbr
CoDel 排隊(duì)論簡(jiǎn)讀:https://blog.csdn.net/dog250/article/details/72849893
TCP-BBR:?https://queue.acm.org/detail.cfm?id=3022184
https://www.net.in.tum.de/fileadmin/bibtex/publications/papers/IFIP-Networking-2018-TCP-BBR.pdf
https://blog.csdn.net/russell_tao/article/details/98723451
Golang進(jìn)階訓(xùn)練營之微服務(wù)可用性設(shè)計(jì)-毛劍
