基于系統(tǒng)負(fù)載的動(dòng)態(tài)限流組件 dynamic-limiter
背景
一個(gè)系統(tǒng)的處理能力是有限的,當(dāng)請求量超過處理能力時(shí),通常會(huì)引起排隊(duì)析珊,造成響應(yīng)時(shí)間迅速提升碘梢。如果對服務(wù)占用的資源量沒有約束州邢,還可能因?yàn)橄到y(tǒng)資源占用過多而宕機(jī)杉武。因此辙诞,為了保證系統(tǒng)在遭遇突發(fā)流量時(shí),能夠正常運(yùn)行轻抱,需要為你的服務(wù)加上限流飞涂。
通常限流可以分為兩類:單機(jī)限流、全局限流祈搜。常見的單機(jī)限流工具有 Guava RateLimiter 和 Java Semaphore较店,全局限流可以用 Redis 做全局計(jì)數(shù)器來實(shí)現(xiàn),基礎(chǔ)架構(gòu)組也提供了一個(gè)靈活的全局限流組件 common-blocking容燕。這些限流工具有一個(gè)共同的缺點(diǎn):都需要手動(dòng)設(shè)置一個(gè)固定的限流閾值梁呈。
首先,手動(dòng)設(shè)置固定閾值需要做容量評估蘸秘,準(zhǔn)確的容量評估是比較難的捧杉。其次,在每次系統(tǒng)更新升級后秘血,閾值會(huì)變得不再準(zhǔn)確味抖,需要重新調(diào)整,比較繁瑣灰粮。再次仔涩,固定閾值也不能應(yīng)對服務(wù)器性能波動(dòng)的情況,對于一些日志量比較大的應(yīng)用粘舟,整點(diǎn)日志壓縮時(shí)熔脂,會(huì)消耗較多性能,此時(shí)系統(tǒng)的處理能力肯定比其他時(shí)候要稍差一些柑肴。最后霞揉,應(yīng)用大多運(yùn)行在虛擬機(jī)上,同一個(gè)實(shí)體機(jī)上的虛擬機(jī)之間也會(huì)相互影響晰骑,這個(gè)體現(xiàn)在監(jiān)控上就是 CPU 使用率里的 steal 值了适秩。
既然固定閾值有這么多缺點(diǎn),我們就想有沒有什么辦法能夠自動(dòng)計(jì)算限流閾值呢硕舆?下面介紹一下:基于系統(tǒng)負(fù)載的動(dòng)態(tài)限流秽荞。
動(dòng)態(tài)限流原理
為什么叫動(dòng)態(tài)限流呢?因?yàn)槲覀兿M谙到y(tǒng)運(yùn)行時(shí)抚官,限流閾值能夠根據(jù)實(shí)際情況做動(dòng)態(tài)調(diào)整扬跋。具體根據(jù)什么來調(diào)整呢?系統(tǒng)負(fù)載凌节,這里我們使用了最常見的三種監(jiān)控指標(biāo):CPU 使用率钦听、Load 和服務(wù)的響應(yīng)時(shí)間洒试。
動(dòng)態(tài)限流的目標(biāo)是,計(jì)算一個(gè)合理的閾值朴上,讓系統(tǒng)在提供最大處理能力的同時(shí)儡司,保持健壯,不被壓垮余指。
動(dòng)態(tài)限流的基本思路可以看下面這幅圖,系統(tǒng)負(fù)載反過來說就是系統(tǒng)的健康程度跷坝。當(dāng)系統(tǒng)負(fù)載較低酵镜,處于健康狀態(tài)時(shí)不限流。當(dāng)系統(tǒng)負(fù)載稍高柴钻,處于不健康狀態(tài)時(shí)淮韭,以最近幾秒處理請求的 QPS 計(jì)算限流閾值。當(dāng)系統(tǒng)負(fù)載過高贴届,狀態(tài)惡化時(shí)靠粪,讓限流閾值以一定的系數(shù)進(jìn)行衰減,直到系統(tǒng)負(fù)載降低毫蚓,系統(tǒng)狀態(tài)由惡化變?yōu)椴唤】嫡技罱K讓系統(tǒng)負(fù)載收斂在兩個(gè)負(fù)載閾值之間。
前面提到在健康狀態(tài)下不限流元潘,那么系統(tǒng)在從健康狀態(tài)變?yōu)椴唤】祷驉夯癄顟B(tài)時(shí)畔乙,就需要計(jì)算一個(gè)初始限流閾值,初始限流閾值的計(jì)算參考了健康狀態(tài)的 QPS 和當(dāng)前處理請求的 QPS翩概。具體的計(jì)算公式如下圖所示牲距,其中 H 表示健康狀態(tài)下的 QPS,C 表示當(dāng)前處理請求的 QPS钥庇。
其中的重點(diǎn)是系統(tǒng)狀態(tài)從健康變成惡化時(shí)的閾值計(jì)算牍鞠,限流閾值等于 H 乘以一個(gè)系數(shù),這個(gè)系數(shù)是 C 除以 H 的二分之一次方评姨,也就是流量暴漲倍數(shù)的二分之一次方难述。這樣計(jì)算的目的,是避免像下圖這樣的情況吐句,初始閾值設(shè)置的不合理時(shí)龄广,限流閾值收斂到合理區(qū)間太慢,浪費(fèi)系統(tǒng)資源蕴侧。
初始閾值設(shè)定之后择同,還需要根據(jù)系統(tǒng)負(fù)載進(jìn)行動(dòng)態(tài)調(diào)整,如何動(dòng)態(tài)調(diào)整呢净宵?可以先看下面這幅閾值調(diào)整示意圖敲才,相比之前的基本思路圖裹纳,這里多了一個(gè)負(fù)載閾值 0,設(shè)置它的目的是希望當(dāng)初始閾值設(shè)置不合理導(dǎo)致系統(tǒng)負(fù)載變得很低時(shí)紧武,能夠快速提升閾值剃氧。當(dāng)系統(tǒng)負(fù)載接近收斂區(qū)間時(shí),進(jìn)行細(xì)微調(diào)整阻星,避免步子邁得太大朋鞍,把系統(tǒng)搞垮了。簡單說就是當(dāng)系統(tǒng)負(fù)載低的時(shí)候妥箕,快速調(diào)整滥酥,當(dāng)系統(tǒng)負(fù)載高的時(shí)候,細(xì)微調(diào)整畦幢。
在實(shí)際中坎吻,負(fù)載閾值 1 和 2 可以靈活配置,為了減少配置工作量宇葱,負(fù)載閾值 0 固定為負(fù)載閾值 1 的 70%瘦真。
最后再說一下,何時(shí)不再限流黍瞧,恢復(fù)正常呢诸尽?當(dāng)突發(fā)流量消失,系統(tǒng)能夠處理全部請求印颤,并且處于健康狀態(tài)時(shí)弦讽,不再限流。
到這里動(dòng)態(tài)限流的原理就講完了膀哲,下面我們看一下線上測試效果往产。
測試效果
最初我們做了基于 Load 的動(dòng)態(tài)限流,服務(wù)器 CPU 是 4 核的某宪,所以兩個(gè)負(fù)載閾值分別設(shè)置成 3 和 5仿村,限流閾值更新頻率為 1 秒一次。
實(shí)際效果請看下面的監(jiān)控圖兴喂,左邊是 10 倍流量壓測而未開限流的情況蔼囊,未開限流時(shí) CPU 使用率高達(dá) 99%,Load 也高達(dá) 20衣迷。中間打開限流之后畏鼓,Load 降到 5 左右,CPU 使用率也降了下去壶谒,但是波動(dòng)很大云矫,為什么呢?想到之前看過的文章里提到 Load 是 5 秒采樣一次汗菜,而這里閾值 1 秒更新一次让禀,更新太快了挑社,更新之后還沒有體現(xiàn)在 Load 計(jì)算上就又更新了。
當(dāng)我們將閾值更新頻率改為 10 秒一次時(shí)巡揍,從下圖可以看出來痛阻,CPU 和 Load 的波動(dòng)小了很多。
看監(jiān)控我們發(fā)現(xiàn) Load 在 3 到 5 之間波動(dòng)時(shí)腮敌,CPU 使用率才 60%阱当,還有提高的空間。我們知道 Load 和 CPU 不同步的原因是糜工,Load 不僅和計(jì)算有關(guān)弊添,也和 IO 有關(guān)。而報(bào)價(jià)是計(jì)算密集型的應(yīng)用啤斗,所以我們又試驗(yàn)了基于 CPU 使用率的動(dòng)態(tài)限流。
我們將閾值設(shè)定為 70 到 90赁咙,看下面的監(jiān)控圖钮莲,CPU 使用率基本穩(wěn)定在 70 到 90 之間,Load 稍微高一些彼水。壓測之后搜索耗時(shí)從 70 漲到了 150 并保持穩(wěn)定崔拥,穩(wěn)定就表示服務(wù)是正常的。
下面我們再看一下凤覆,基于 CPU 和基于 Load 限流時(shí)搜索成功量的對比链瓦,分別是 164 和 134,說明基于 CPU 的限流的確提升了系統(tǒng)處理能力盯桦,提高了資源利用效率慈俯。
一些服務(wù)可能對響應(yīng)時(shí)間比較敏感,所以我們又做了基于時(shí)間的動(dòng)態(tài)限流拥峦,當(dāng)我們將閾值設(shè)定在 140 到 200 之間時(shí)贴膘,看監(jiān)控壓測之后搜索耗時(shí)也基本穩(wěn)定在這個(gè) 140 到 200 之間,CPU 和 Load 監(jiān)控也保持穩(wěn)定略号。
總結(jié)
我們將上述講的基于負(fù)載的動(dòng)態(tài)限流封裝到了一個(gè) API dynamic-limiter 中刑峡,供各個(gè)系統(tǒng)使用。最后總結(jié)一下玄柠,動(dòng)態(tài)限流適合什么樣的場景呢突梦?
- 如果你的系統(tǒng)內(nèi)單個(gè)服務(wù)占用大部分資源,就可以使用基于 CPU 或 Load 的動(dòng)態(tài)限流羽利。
- 如果你的服務(wù)對響應(yīng)時(shí)間要求比較高宫患,可以使用基于時(shí)間的動(dòng)態(tài)限流。
實(shí)際中这弧,也可以同時(shí)參考多種因素來進(jìn)行動(dòng)態(tài)限流撮奏,起到一個(gè)多重約束的作用俏讹。比如同時(shí)使用 CPU 和 TIME 時(shí),表示既對 CPU 使用率有一個(gè)硬約束畜吊,又對服務(wù)響應(yīng)時(shí)間有一個(gè)硬約束泽疆。